2012-11-17 106 views
7

应老板的要求,我创建了一小组脚本,用于定期监视某些设备和进程的状态。随后使用相对复杂的VBA模块处理此信息,该模块收集所有信息,应用公式,设置范围并生成图表等。C#和VBA之间的通信

但有两个问题: 我是业余程序员,所以我的VBA例程效率很低。这对我来说并不是什么大问题(我只是在运行时起床喝咖啡),但对于其他用户来说,这可能是一件麻烦事,因为他们不知道为什么会这么长时间。我想要一个进度的图形表示。 应用程序的配置通过文本文件完成。我想提供一个像样的GUI来设置配置。

为了达到这个目的,我正在寻找一种方法让我的C#WinForms应用程序与我的VBA应用程序进行通信。我知道如何从我的C#应用​​程序中运行VBA例程,但我不知道如何让它们实时地进行通信。

这里有两件事我特别想实现:

  • 我已经拥有了所保存在VBA程序结束的日志文件。 取而代之的是不过我想发送日志/调试,消息 实时我的C#应用​​程序(而不是仅仅在 应用程序的结束),这样的消息可以显示在我的GUI应用程序,因为他们 由VBA应用程序生成。
  • 我也想了VBA应用发送有关实时进度信息 我的GUI应用程序,所以我可以在我的GUI应用程序的图形 进度条

我已经想过通过标准输出进行通信。我知道如何使用C#/ .Net从标准输出中读取数据,但我不确定如何使用VBA写入StdOut数据流。我相信很多人会指出,我试图达到的是愚蠢和老式的(或完全不必要的),但作为一个业余程序员,它似乎对我来说是一个非常具有挑战性和有趣的项目,可以教我一两件事。

+2

我建议你花点时间让VBA应用程序更好。另外,没有理由不能使用进度条并登录VBA。为什么介绍额外的复杂性,如果你不需要? –

+0

嗯,首先,我认为我不会创建一个更快或更高效的应用程序,因为我几乎全部使用了VBA的所有知识。正如我所说,我是一个绝对的业余爱好者。第二:在工作簿实际完全生成之前,我希望“Excel层”不可见。第三:我在VBA中创建表单的经验绝对没有,但我在C#中有一些经验。第四:正如我所说,这似乎是一件非常有趣的事情。 –

+0

我不明白你是如何抵达你已经学习了用C#编程的形式,而是用VBA编程'其他'的情况?将所有这些运行在一个平台或另一个平台上是不是更有意义?我建议让它在C#中运行,因为它是更新的技术。但是,如果它只是形成,你需要构建一个完整的VBA项目,在VBA中,表单确实非常简单。当你遇到困难时,只需尝试一下并提出问题。这将比试图在VBA之间引发事件并将它们吸收在.NET中简单得多.Net –

回答

7

创建一个C#COM可见的类很容易,并且(如您在评论中所说)很有趣。这是一个小样本。

在一个新的C#库项目,添加:

using System; 
using System.Runtime.InteropServices; 
using System.Windows.Forms; 

namespace CSharpCom 
{ 
    [ComVisible(true)] 
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] 
    //The 3 GUIDs in this file need to be unique for your COM object. 
    //Generate new ones using Tools->Create GUID in VS2010 
    [Guid("18C66A75-5CA4-4555-991D-7115DB857F7A")] 
    public interface ICSharpCom 
    { 
     string Format(string FormatString, [Optional]object arg0, [Optional]object arg1, [Optional]object arg2, [Optional]object arg3); 
     void ShowMessageBox(string SomeText); 
    } 

    //TODO: Change me! 
    [Guid("5D338F6F-A028-41CA-9054-18752D14B1BB")] //Change this 
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] 
    interface ICSharpComEvents 
    { 
     //Add event definitions here. Add [DispId(1..n)] attributes 
     //before each event declaration. 
    } 

    [ComVisible(true)] 
    [ClassInterface(ClassInterfaceType.None)] 
    [ComSourceInterfaces(typeof(ICSharpComEvents))] 
    //TODO: Change me! 
    [Guid("C17C5EAD-AA14-464E-AD32-E9521AC17134")] 
    public sealed class CSharpCom : ICSharpCom 
    { 
     public string Format(string FormatString, [Optional]object arg0, [Optional]object arg1, [Optional]object arg2, [Optional]object arg3) 
     { 
      return string.Format(FormatString, arg0, arg1, arg2, arg3); 
     } 

     public void ShowMessageBox(string SomeText) 
     { 
      MessageBox.Show(SomeText); 
     } 
    } 
} 

您将要进入你的项目属性,在“签名”选项卡,选中签署您的集会,并创建一个新的“强名密钥文件“。这将有助于防止注册DLL的版本问题。

编译它,并在Visual Studio命令提示符下使用regasm注册该DLL。您将使用32位或64位regasm取决于你使用的是什么版本的Office的......你会发现RegAsm在C:\windows\Microsoft.NET\FrameworkC:\windows\Microsoft.NET\Framework64(32位和64位,分别为):

C:\windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /codebase /tlb CSharpCom.dll

这将在Windows上注册你的DLL,所以现在在VBA编辑器中,你应该可以进入Tools-> References并找到你的COM的命名空间“CSharpCom”。检查框,现在你应该可以在VBA来创建你的COM对象:

Sub TestCom() 
    Dim c As CSharpCom.CSharpCom 
    Set c = New CSharpCom.CSharpCom 
    c.ShowMessageBox c.Format("{0} {1}!", "Hello", "World") 
End Sub 

您可以添加表格到您的COM对象,并打开它们时,VBA调用特定的方法;你应该可以使用它来创建一个带有进度条的表单。但有一点需要考虑的是,VBA是单线程的。这意味着当你的代码运行时,其他的东西都会被冻结,所以事情可能会变得棘手。你可以在你的COM可见类中创建3个方法:一个打开表单,一个更新进度(在VBA调用update()方法后立即调用VBA的DoEvents()方法在您的COM对象上,以允许Office处理屏幕更新),以及一个关闭它。你应该在窗体上调用Dispose()。这可以在“close”方法中完成,但我认为如果你的VBA崩溃,并且你的close方法从未被调用,那么它可能会导致内存泄漏/问题 - 只是别的可以考虑的事情。

+0

在我看来,他试图在C#程序中捕获VB中引发的事件,而不是相反。 –

+0

是的,多数民众赞成在这么做...... COM可见的C#对象是**写入**在C#和**实例化**在VBA(或任何地方,真的 - 这就是他们的目的)。这反过来也不行。 – transistor1

+0

这确实做了我所问的,尽管这是一种非常先进的技术。对于这些不同的解决方案,我有很多乐趣,但最终我(可以预料)得出结论:对我的“应用程序”进行完全的反思(如果这样的名字确实可以应用于我在这里看到的内容) 是必须的。感谢您的输入,我确实在这里学到了很多东西! –

5

您可以在您的VB6项目中使用.NET DLL,并创建您在程序集中定义的任何对象的实例。这是通过创建一个类型库文件(.tlb)注册.NET程序集(.dll)进行COM互操作(在VB6中使用)来完成的。 .tlb文件包含额外的信息,以便您的VB6项目可以使用您的.dll。请注意,您的VB6项目不会引用.dll,而是引用相应的.tlb文件。您可以使用Regasm.exe实用程序来生成并注册类型库并注册托管程序集的位置。那么它只是创建一个.NET对象的实例,不管它是WinForm还是其他酷类。 =)。

Dim MyDotNetObject As MyDotNetClass 

Set MyDotNetObject = New MyDotNetClass 
MyDotNetObject.SomeMethod(value1, value2) 

''end of execution 
Set MyDotNetObject = Nothing 

参见:http://support.microsoft.com/default.aspx?scid=kb;en-us;817248

+0

在我看来,他试图在C#程序中捕捉VB中引发的事件,而不是相反。 –

4

我相信在这个链接的代码确实大概你想要什么:

  • C#代码创建一个COM对象(这是IE浏览器的例子,但你的描述,你已经设法创建你的VBA例程的实例)

  • 它将.Net事件处理程序(使用+ =)附加到由COM对象(当标题变化)

  • 它定义了样本事件处理代码

http://msdn.microsoft.com/en-us/library/66ahbe6y.aspx

我不自称理解这段代码,我的目的是为了表明费解该解决方案将是!你应该只用一个解决方案来构建它。总的VBA解决方案将会更快地开发(你只需要学习VBA表单很容易),但是老技术。总的C#解决方案开发起来会比较慢,但是随后您将学习C#。

表现实际上是一个问题。如果它现在效率不高,那么当你处理5倍的记录时会发生什么?你应该在之前解决性能问题你得到那么多记录。