2009-08-15 6 views
2

Je teste l'hébergement PowerShell en utilisant C#. Voici une application console qui fonctionne:Passage d'une variable du volet des tâches personnalisées Excel 2007 à PowerShell hébergé

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

Je crée d'abord une instance Excel.Application et de le transmettre à l'instance PowerShell hébergé en tant que varible nommé Application $. Cela fonctionne et je peux utiliser cette variable comme si Excel.Application a été créé à partir de PowerShell. J'ai ensuite créé un addin Excel en utilisant VS 2008 et ajouté un contrôle utilisateur avec deux zones de texte et un bouton à l'addin (le contrôle utilisateur apparaît comme un volet de tâches personnalisé au démarrage d'Excel). L'idée était la suivante: lorsque je clique sur le bouton, une instance PowerShell hébergée est créée et je peux y passer l'instance Excel.Application en cours, comme dans le premier exemple, afin de pouvoir utiliser cette variable pour automatiser Excel depuis PowerShell (une zone de texte sera utilisé pour l'entrée et l'autre pour la sortie Voici le 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; 
      } 
     } 
    } 
} 

le code est similaire au premier échantillon, sauf que l'instance de Excel.Application est disponible à la Addin via Globals.ThisAddIn.Application (VSTO généré) et je peux voir que c'est vraiment une instance Microsoft.Office.Interop.Excel.Application parce que je peux utiliser des choses comme app.ActiveCell.Value2 = "Test" (cela met en fait le texte dans la cellule active.) Mais quand je passe l'instance Excel.Application à l'instance PowerShell Ets il y a une instance de System .__ ComObject et je n'arrive pas à comprendre comment le convertir en Excel.Application. Lorsque j'examine la variable à partir de PowerShell en utilisant $ Application | Get-Member c'est la sortie que je reçois dans la deuxième zone de texte:

 
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() 

Ma question est de savoir comment puis-je passer une instance de Microsoft.Office.Interop.Excel.Application d'un VSTO généré Excel 2007 Addin à un instance PowerShell hébergée, afin que je puisse le manipuler à partir de PowerShell?

(je l'ai déjà posté la question dans le forum Microsoft C# sans réponse)

Répondre

1

Comme l'a souligné keith-hill, le problème semble être que Powershell ne peut pas trouver d'informations de bibliothèque de type. Une façon de contourner le problème consiste à utiliser la méthode InvokeMember pour fonctionner sur l'instance System.__ComObject.De cette façon, vous pouvez manipuler l'objet directement. This post a une meilleure explication avec des exemples, mais en utilisant ADSI plutôt qu'Excel.

+0

Merci pour la réponse. InvokeMember fonctionne certainement, par exemple: Pipeline pipeline = runspace.CreatePipeline ("[System .__ ComObject] .InvokeMember (\" Version \ ", [System.Reflection.BindingFlags] :: GetProperty, $ null, $ Application, $ null) Out-String "); renvoie '12 .0 'Mais s'il n'y a aucun moyen de lancer $ Application vers Excel.Application cela complique les choses. L'idée était de passer Excel.Application de l'addin à PowerShell et de l'utiliser simplement comme s'il avait été créé dans PowerShell. –

0

Ma première pensée est que l'instance d'exécution PowerShell pourrait ne pas avoir l'ensemble Interop chargé:

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

obtenez-vous une erreur si vous essayez une distribution explicite?

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

Vous risquez également d'être confronté à un problème de threading si l'objet requiert le mode STA. Avec v2 PowerShell, vous pouvez essayer ceci:

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

Ou si UseCurrentThread ne fonctionne pas:

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

Vous pouvez en savoir plus sur l'utilisation STA avec PowerShell dans this post.

+0

Merci pour la réponse. J'ai essayé d'ajouter l'assembly et de lancer $ Application à Interop.Excel.Application, mais j'ai l'exception 'Can not convert ...'. Ni PSThreadOptions.UseCurrentThread ni PSThreadOptions.ReuseThread fonctionnent, je reçois toujours System .__ ComObject pour $ Application type dans PowerShell. –

2

Il semble que le type que vous obtenez de Globals.ThisAddin.Application est un proxy transparent/distant (System.Runtime.Remoting.Proxies .__ TransparentProxy). Apparemment, PowerShell a du mal à trouver les informations de la bibliothèque de types.

+0

J'avoue que c'est hors de ma ligue. Globals.ThisAddin.Application est déclaré dans ThisAddin.Designer.cs en tant que Microsoft.Office.Interop.Excel.Application, mais lors du débogage, le type indiqué est System.Runtime.Remoting.Proxies .__ TransparentProxy. Y a-t-il un moyen de le faire fonctionner? –

Questions connexes