Pasar una variable de un panel de tareas personalizado Excel 2007 para PowerShell Alojado
-
16-09-2019 - |
Pregunta
Estoy probando PowerShell alojamiento con C #. Aquí es una aplicación de consola que funciona:
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);
}
}
}
}
La primera vez que se crea una instancia Excel.Application y pasarlo a la instancia PowerShell acogido como un llamado varible $ Aplicación. Funcionado y se puede utilizar esta variable como si Excel.Application fue creado desde dentro de PowerShell.
Me próxima creado un complemento de Excel utilizando VS 2008 y añadido un control de usuario con dos cuadros de texto y un botón para el complemento (el control de usuario aparece como un panel de tareas personalizado al iniciarse Excel). La idea era la siguiente: cuando haga clic en el botón se crea una instancia de PowerShell alojado y me puede pasar a la instancia que Excel.Application actual como una variable, al igual que en la primera muestra, para que pueda utilizar esta variable para automatizar Excel desde PowerShell (un cuadro de texto se utiliza para la entrada y la otra para la salida Aquí está el código:.
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;
}
}
}
}
El código es similar a la primera muestra, salvo que la instancia actual de Excel.Application está disponible para el complemento a través de Globals.ThisAddIn.Application (VSTO genera) y puedo ver que es realmente un Microsoft.Office.Interop. Excel.Application ejemplo, porque se puede usar cosas como app.ActiveCell.Value2 = "Test" (esto realmente pone el texto en la celda activa). Pero cuando pase la instancia Excel.Application a la instancia PowerShell lo que llega allí es una instancia del Sistema .__ ComObject y no puedo encontrar la manera de echarlo a Excel.Application. Cuando examino la variable de PowerShell utilizando $ aplicaciones | Get-Member esta es la salida me meto en el segundo cuadro de texto:
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()
Mi pregunta es ¿cómo puedo pasar una instancia de Microsoft.Office.Interop.Excel.Application de un VSTO genera complemento de Excel 2007 a una instancia de PowerShell alojada, por lo que puede manipular desde PowerShell?
(He publicado con anterioridad a la pregunta en el foro de Microsoft C # sin respuesta)
Solución
Como keith-hill
ha señalado, el problema parece ser que Powershell no puede encontrar información de tipo biblioteca. Una forma de evitarlo es utilizar el método InvokeMember
para operar en la instancia System.__ComObject
. De esta manera, se puede manipular el objeto directamente. Este post tiene una mejor explicación con ejemplos, aunque utilizando ADSI en lugar de Excel.
Otros consejos
Parece que el tipo que regrese de Globals.ThisAddin.Application es una interacción remota proxy transparente / (System.Runtime.Remoting.Proxies .__ TransparentProxy). Al parecer PowerShell está teniendo dificultades para encontrar la información de biblioteca de tipos para ello.
Lo primero que pensé es que el espacio de ejecución de PowerShell podría no tener el ensamblado de interoperabilidad cargado:
[void][Reflection.Assembly]::LoadWithPartialName('Microsoft.Office.Interop.Excel')
¿Le da un error si se intenta una conversión explícita?
$excel = [Microsoft.Office.Interop.Excel.Application] $Application
También puede ser que funcione en un tema de roscado si el objeto requiere el modo de STA. Con PowerShell v2, es posible que intente lo siguiente:
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.ThreadOptions = PSThreadOptions.UseCurrentThread;
runspace.Open();
runspace.SessionStateProxy.SetVariable("Application", app);
O si UseCurrentThread
no funciona:
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.ApartmentState = System.Threading.ApartmentState.STA;
runspace.ThreadOptions = PSThreadOptions.ReuseThread;
runspace.Open();
runspace.SessionStateProxy.SetVariable("Application", app);
Puede encontrar más información acerca del uso de STA con PowerShell en este post .