将变量从 Excel 2007 自定义任务窗格传递到托管 PowerShell

Posted

技术标签:

【中文标题】将变量从 Excel 2007 自定义任务窗格传递到托管 PowerShell【英文标题】:Passing a variable from Excel 2007 Custom Task Pane to Hosted PowerShell 【发布时间】:2010-11-19 20:43:05 【问题描述】:

我正在使用 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 实例并将其作为名为 $Application 的变量传递给托管的 PowerShell 实例。这行得通,我可以使用这个变量,就好像 Excel.Application 是从 PowerShell 中创建的一样。

接下来我使用 VS 2008 创建了一个 Excel 插件,并向插件添加了一个带有两个文本框和一个按钮的用户控件(当 Excel 启动时,用户控件显示为自定义任务窗格)。想法是这样的:当我单击按钮时,会创建一个托管的 PowerShell 实例,我可以将当​​前 Excel.Application 实例作为变量传递给它,就像在第一个示例中一样,所以我可以使用这个变量从 PowerShell 自动化 Excel (一个文本框用于输入,另一个用于输出。代码如下:

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 这是我在第二个文本框中得到的输出:

类型名称:System.__ComObject 名称 MemberType 定义 ---- ---------- ---------- CreateObjRef 方法 System.Runtime.Remoting.ObjRef CreateObj... 等于方法 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 操作它?

(我之前在微软C#论坛发过这个问题没有答案)

【问题讨论】:

【参考方案1】:

您从 Globals.ThisAddin.Application 返回的类型似乎是透明/远程代理 (System.Runtime.Remoting.Proxies.__TransparentProxy)。显然 PowerShell 很难找到它的类型库信息。

【讨论】:

我承认这不合我意。 Globals.ThisAddin.Application 在 ThisAddin.Designer.cs 中声明为 Microsoft.Office.Interop.Excel.Application,但在调试时显示的类型是 System.Runtime.Remoting.Proxies.__TransparentProxy。有什么办法让它工作吗?【参考方案2】:

正如keith-hill 所指出的,问题似乎是Powershell 找不到类型库信息。绕过它的一种方法是使用InvokeMember 方法对System.__ComObject 实例进行操作。这样,您可以直接操作对象。 This post 有更好的示例解释,尽管使用的是 ADSI 而不是 Excel。

【讨论】:

感谢您的回答。 InvokeMember 确实有效,例如: Pipeline pipeline = runspace.CreatePipeline("[System.__ComObject].InvokeMember(\"Version\",[System.Reflection.BindingFlags]::GetProperty,$null,$Application,$null) |外串");返回 '​​12.0' 但是如果没有办法将 $Application 转换为 Excel.Application 这会使事情复杂化。我们的想法是将 Excel.Application 从插件传递到 PowerShell,并像在 PowerShell 中创建一样简单地使用它。【参考方案3】:

我的第一个想法是 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);

您可以在this post 中了解有关将 STA 与 PowerShell 结合使用的更多信息。

【讨论】:

感谢您的回复。我尝试添加程序集并将 $Application 转换为 Interop.Excel.Application,但出现“无法转换...”异常。 PSThreadOptions.UseCurrentThread 和 PSThreadOptions.ReuseThread 都不起作用,我仍然在 PowerShell 中获得 $Application 类型的 System.__ComObject。

以上是关于将变量从 Excel 2007 自定义任务窗格传递到托管 PowerShell的主要内容,如果未能解决你的问题,请参考以下文章

打开约会项目时显示自定义任务窗格(会议发生和个人会议)

加载DOM环境

Excel-DNA项目只用1个文件实现Ribbon CustomUI和CustomTaskpane定制C#版

从 UIViewController 将变量传递给自定义 UITableViewCell

Excel催化剂开源第5波-任务窗格在OFFICE2013中新建文档不能同步显示问题解决

Excel-DNA项目只用1个文件实现Ribbon CustomUI和CustomTaskpane定制VB.Net版