65.9K
CodeProject 正在变化。 阅读更多。
Home

回调函数和 .NET C# COM 组件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.36/5 (7投票s)

2008年2月1日

CPOL

3分钟阅读

viewsIcon

104264

downloadIcon

897

如何让您的 C# 组件回调 Perl 子例程

引言

最近,我的合作者需要将一个 C# 类库转换为一个 COM 兼容组件,该组件可以从 C++/C#/Perl/脚本环境中调用。 已经有很多关于如何实现这方面的信息,但他有一个更复杂的要求。 该要求是他的 Perl 脚本需要调用一个 C# 函数(例如 Analyze())。 Analyze() 函数通常需要很长时间来处理,并且客户端需要定期更新进度计数器。 最好使用回调来解决这个问题。

然而,很少有现成的资料可以解释如何实现这一点。

下面的文章解释了如何在 Perl 环境中处理多个 .NET 事件。

背景

本文试图解释适用于 COM 客户端的事件源和事件接收器的概念。

代码包含一个 C# COM 服务器和一个示例 Perl 客户端,它以最简单的方式解释了回调函数的概念。

本文通过展示如何使用 Perl 中存在的 Win32::OLE 模块,并使用它来捕获 .NET 组件中生成的事件,来实现上述目的。

Using the Code

要使用附带的代码,需要执行几个步骤。

  1. 解压缩 CSharp_and_Scripting_New_src.zip
  2. 浏览到 CSharp_and_Scripting_New\CallBack Server\CallBack 文件夹。
  3. 运行 register.bat
    1. 如果 register.bat 脚本对您不起作用,请在 Visual Studio 2005 中打开项目 CallBack.sln。 转到“工具”菜单 -> Visual Studio 2005 命令提示符。
    2. 在命令窗口中将目录更改为 CSharp_and_Scripting_New\CallBack Server\CallBack\bin\debug 文件夹。
    3. 键入 regasm CallBack.dll /tlb:Callback.tlb
    4. 键入 gacutil -i CallBack.dll
  4. 现在,.NET C# COM 服务器已在您的系统中注册,您可以运行 CSharp_and_Scripting_New\CallBack Client\Sample1.pl 以查看如下所示的输出

    CSharp_and_Scripting_New_Snapshot.png

描述

COM 服务器代码

COM 服务器已用 C# 编写。 C# 没有函数指针的直接概念。 相反,它定义了称为 delegate 的东西,它是对函数的安全引用形式。 现在为了利用 delegate 的强大功能,让我们在我们的类 DotNetEventSender 中定义一些 delegate,如下所示

// No need to show this delegate to COM
[ComVisible(false)]
public delegate void MyEventTarget(string msg);
[ComVisible(false)]
public delegate void MyEventTarget2(int nTimerCounter);

public event MyEventTarget TheEvent;
public event MyEventTarget2 TheEvent2;  

如您所见,我们在类中声明了两种不同类型的 delegate,并将两个事件与这些 delegate 关联起来。 现在让我们声明一个接口,我们的类将公开这个接口

//This interface defines purely the events.
//DotNetEventSender should implement this interface with the
//ComSourceInterfaces() attribute to become an Event Source.
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]

public interface CallBackEventInterface
{
    [DispId(1)] void TheEvent(string msg); 
    [DispId(2)] void TheEvent2(int nTimerCounter);
}  

然后,我们的类将公开此接口,如下所示

//Identifies these interfaces that are exposed as COM event sources 
//for the attributed class.
[ComSourceInterfaces(typeof(CallBackEventInterface))]

//Tells the compiler not to generate an interface automatically and that we are 
//implementing our own interface (IDotNetEventSender)
[ClassInterface(ClassInterfaceType.None)]
public class DotNetEventSender:IDotNetEventSender
{ ... 

注意属性标签

[ComSourceInterfaces(typeof(CallBackEventInterface))] 

这告诉编译器我们有兴趣为我们的类公开一个事件源。 然后,事件接收器将在 Perl 客户端中实现,该客户端包明确地消耗(处理)我们的 C# 服务器生成的所有事件。

现在,如果没有实现 Perl 客户端可以调用的某些服务器函数,那么公开事件源就没什么用了。 例如,Perl 客户端将调用函数 Run(),这将启动 C# 服务器中的计时器。 然后,该计时器将间歇性地触发事件源,然后可以在 Perl 客户端中处理这些事件源。

因此,现在让我们在 C# 服务器中实现一些类函数。

private int m_nLoopCounter=0;
public void Run()
{
        Console.Out.WriteLine("Inside Run");
        //Note that the event handlers will be specified in the client
        //(e.g. Perl Script)     
 
        //start a timer which call the Events every one second.
        System.Timers.Timer progress = new System.Timers.Timer(1000);
        progress.Elapsed += new ElapsedEventHandler(TimerFunction);           
        progress.Start();
}

private void TimerFunction(object source, ElapsedEventArgs e)
{
        m_nLoopCounter++;
        TheEvent("Hello, World!");
        TheEvent2(m_nLoopCounter);
} 

Perl 客户端代码

在 Perl 客户端代码中,基本步骤是创建 C# 服务器的对象,如下所示

my $TM = Win32::OLE->new('CallBack.DotNetEventSender');

完成此操作后,我们希望通知 C# 服务器,我们有兴趣接收服务器上生成感兴趣的事件时的通知。 我们通过声明来实现这一点

Win32::OLE->WithEvents($TM, 'MyEvents');

在这里,我们通知 C# 服务器所有事件都应转发到包 MyEvents。 该包将定义子例程,其名称与 C# 服务器中的事件完全相同。

package MyEvents;

#The name of the subroutine should be the exactly the same as the name of the
#event in the C# component.

sub TheEvent 
{
        my ($obj,$args) = @_;
        print "TheEvent() : ".$args."\n";
}

sub TheEvent2 
{
    my ($obj,$args) = @_;
    print "TheEvent2(): Timer Fired Count: ".$args."\n";
} 

现在,如果我们不运行一个循环,该循环将使 Perl 客户端等待事件消息到达,那么定义以上所有代码就没什么用了。 为此,我们将代码添加到主包的末尾,如下所示

#keeps the Perl client active, processing the event messages as they
#occur repeatedly. 

Win32::OLE->MessageLoop(); 

就这样,伙计们! 只需运行 sample1.pl 即可查看上述逻辑的实际应用。 祝您“互操作”愉快! :-)

历史

  • 2008 年 2 月 1 日:初始帖子
© . All rights reserved.