2009-08-15 10 views
2

Ich teste PowerShell-Hosting mit C#. Hier ist eine Konsolenanwendung, die funktioniert:Übergeben einer Variablen aus dem benutzerdefinierten Aufgabenbereich von Excel 2007 an Hosted 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); 
      } 
     } 
    } 
} 

ich zum ersten Mal eine Excel.Application Instanz erstellen und an die gehostete Powershell-Instanz als varible namens $ Anwendung übergeben. Das funktioniert und ich kann diese Variable verwenden, als ob Excel.Application von PowerShell aus erstellt wurde.

Ich erstellte als nächstes ein Excel-Add-In mit VS 2008 und fügte ein Benutzersteuerelement mit zwei Textfeldern und einer Schaltfläche zum Add-In hinzu (das Benutzersteuerelement wird als benutzerdefinierter Aufgabenbereich angezeigt, wenn Excel gestartet wird). Die Idee war folgende: Wenn ich auf die Schaltfläche klicke, wird eine gehostete PowerShell-Instanz erstellt und ich kann die aktuelle Excel.Application -Instanz wie im ersten Beispiel als Variable übergeben, sodass ich diese Variable verwenden kann, um Excel aus PowerShell zu automatisieren (ein Textfeld würde für die Eingabe und die andere für die Ausgabe verwendet werden, hier der Code.

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; 
      } 
     } 
    } 
} 

der Code ist ähnlich zu der ersten Probe, mit der Ausnahme, dass die aktuelle Excel.Application Instanz an die addin verfügbar über Globals.ThisAddIn.Application (VSTO generiert) und ich kann sehen, dass es wirklich eine Microsoft.Office.Interop.Excel.Application-Instanz ist, weil ich Dinge wie app.ActiveCell.Value2 = "Test" (das tatsächlich den Text setzt in die aktive Zelle), aber wenn ich die Excel.Application-Instanz an die PowerShell-Instanz überlasse, was g Es gibt eine Instanz von System .__ ComObject und ich kann nicht herausfinden, wie es in Excel.Application umgewandelt wird. Wenn ich die Variable von PowerShell mit $ Application | untersuche Get-Member Dies ist die Ausgabe I im zweiten Textfeld erhalten:

 
TypeName: System.__ComObject 

Name      MemberType Definition 
----      ---------- ---------- 
CreateObjRef    Method  System.Runtime.Remoting.ObjRef CreateObj... 
Equals      Method  System.Boolean Equals(Object obj) 
GetHashCode     Method  System.Int32 GetHashCode() 
GetLifetimeService   Method  System.Object GetLifetimeService() 
GetType      Method  System.Type GetType() 
InitializeLifetimeService Method  System.Object InitializeLifetimeService() 
ToString     Method  System.String ToString() 

Meine Frage ist, wie kann ich eine Instanz von Microsoft.Office.Interop.Excel.Application gehen von einem VSTO Excel 2007 AddIn erzeugt ein gehostet PowerShell-Instanz, so kann ich es von PowerShell manipulieren?

(ich habe geschrieben zuvor die Frage in der Microsoft C# Forum ohne Antwort)

Antwort

1

Wie keith-hill darauf hingewiesen hat, scheint das Problem zu sein, dass Powershell Typ Bibliothek Informationen nicht finden kann. Eine Möglichkeit, dies zu umgehen, ist die Verwendung der InvokeMember-Methode für die System.__ComObject-Instanz.Auf diese Weise können Sie das Objekt direkt manipulieren. This post hat eine bessere Erklärung mit Beispielen, obwohl mit ADSI statt Excel.

+0

Vielen Dank für die Antwort. InvokeMember funktioniert sicherlich, zum Beispiel: Pipeline pipeline = runspace.CreatePipeline ("[System .__ ComObject] .InvokeMember (\" Version \ ", [System.Reflection.BindingFlags] :: GetProperty, $ null, $ Application, $ null) | Out-String "); return '12 .0 'Aber wenn es keine Möglichkeit gibt, $ Application in Excel.Application zu casten, kompliziert dies die Dinge. Die Idee war, Excel.Application vom Add-In an PowerShell zu übergeben und einfach so zu verwenden, als wäre es in PowerShell erstellt worden. –

0

Mein erster Gedanke ist, dass der Runspace Powershell könnte die Interop-Assembly nicht geladen haben:

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

Haben Sie bekommen ein Fehler, wenn Sie eine explizite Besetzung versuchen?

Möglicherweise treten auch Threading-Probleme auf, wenn das Objekt den STA-Modus erfordert. Mit Powershell v2, könnten Sie dies versuchen:

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

Sie erfahren Sie mehr über die Verwendung von STA mit Powershell in this post können:

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

Oder wenn UseCurrentThread nicht funktioniert.

+0

Danke für die Antwort. Ich habe versucht, die Assembly hinzuzufügen und $ Application zu Interop.Excel.Application zu übertragen, aber ich bekomme die Ausnahme 'Can not convert ...'. Weder PSThreadOptions.UseCurrentThread noch PSThreadOptions.ReuseThread funktionieren, ich bekomme immer noch System .__ ComObject für $ Application-Typ in PowerShell. –

2

Es scheint, dass der Typ, den Sie von Globals zurückbekommen. ThisAddin.Application ist ein transparenter/remoter Proxy (System.Runtime.Remoting.Proxies .__ TransparentProxy). Scheinbar hat PowerShell Schwierigkeiten, die Typbibliotheksinfo dafür zu finden.

+0

Ich gebe zu, das ist außerhalb meiner Liga. Globals.ThisAddin.Application wird in ThisAddin.Designer.cs als Microsoft.Office.Interop.Excel.Application deklariert, aber beim Debuggen des angezeigten Typs ist System.Runtime.Remoting.Proxies .__ TransparentProxy. Gibt es eine Möglichkeit, es zum Laufen zu bringen? –