.NET与COM互操作性:在VB6中动态绑定.NET组件

.NET与COM的互操作性领域,有许多示例和文章,但它们往往忽略了在运行时附加事件的重要性。本文将介绍如何构建一个.NET组件,将其标记为COM可见,并在VB6中运行时使用它并附加到其事件上。

.NET- COM互操作性

KRISHNA PRASAD.N.撰写了一篇出色的文章,名为“.NET- COM互操作性”,其中详细解释了从COM客户端调用.NET组件、.NET Marshalling、Interop marshaler、COM marshaler等问题。

如果需要了解如何一步步创建C#COM互操作DLL,可以参考RakeshGunijan的“C# COM”。

早期绑定与晚期绑定

早期绑定和晚期绑定各有优缺点。通常更倾向于晚期绑定,尤其是当使用别人的API时,因为晚期绑定的优势在于可以减少一些版本依赖性。如果需要使用或分发每月更新的API或应用程序,相信,晚期绑定是更好的选择。

什么是函数指针

函数指针是一种约定,它允许将用户定义的函数的地址作为参数传递给另一个函数,以便在应用程序中使用。

选择

将构建一个COM互操作,从VB6运行时使用它(无需向项目添加任何引用)并附加到其事件。以下是选择:

从VB6到C#传递事件地址作为函数指针,并从C#调用它。(感谢Misha Shneerson在MSDN论坛上解决这个问题。)

public void AttachToAnEventMethod1(int address) { CallBackFunction cb = (CallBackFunction)Marshal.GetDelegateForFunctionPointer( new IntPtr(address), typeof(CallBackFunction)); SetCallbackMethod1(cb); }

VB6向C#传递消费者对象和回调函数的名称,并通过反射从C#调用该函数。

object objApp_Late = this.consumer; Type objClassType = this.consumer.GetType(); object ret = objApp_Late.GetType().InvokeMember( this.callbackEventName, BindingFlags.InvokeMethod, null, objApp_Late, null);

构建C# COM

将构建一个简单的类用于测试目的。目标是附加到一个事件,当在运行时执行MultiplyAndNotify方法时,从VB6获得反馈。

[ComVisible(true)] [ClassInterface(ClassInterfaceType.None)] [ProgId("CSharpApi.CsApiCore")] [ComSourceInterfaces(typeof(NotifyInterface))] public class CsApiCore : _CsApiCore { private string sessionID = ""; public string SessionID { get { return sessionID; } set { sessionID = value; } } public event NotifyDelegate NotifyEvent; public CsApiCore() { this.SessionID = System.Guid.NewGuid().ToString().ToUpper(); } public int Add(int a, int b) { return a + b; } public int MultiplyAndNotify(int a, int b) { for (int i = 0; i < 2; i++) { Thread.Sleep(500); notifyClients(); } return a * b; } private void notifyClients() { if (NotifyEvent != null) { NotifyEvent(); } if (this.consumer != null) { object objApp_Late = this.consumer; Type objClassType = this.consumer.GetType(); object ret = objApp_Late.GetType().InvokeMember( this.callbackEventName, BindingFlags.InvokeMethod, null, objApp_Late, null); } if (this.cb != null) { cb.DynamicInvoke(); } } private object consumer = null; private string callbackEventName = ""; public void AttachToAnEventMethod2(object consumerObject, string eventName) { this.consumer = consumerObject; this.callbackEventName = eventName; } public void DetachMethod2() { this.callbackEventName = ""; this.consumer = null; } public void AttachToAnEventMethod1(int address) { CallBackFunction cb = (CallBackFunction)Marshal.GetDelegateForFunctionPointer( new IntPtr(address), typeof(CallBackFunction)); SetCallbackMethod1(cb); } public void DetachMethod1() { this.cb = null; } private Delegate cb = null; public void SetCallbackMethod1([MarshalAs(UnmanagedType.FunctionPtr)] CallBackFunction callback) { this.cb = callback; } }

使用C# COM从VB6

首先需要使用晚期绑定连接到C#COM

Dim apiHandler As Object Dim obj As Object Set obj = CreateObject("CSharpApi.CsApiCore") If obj Is Nothing Then MsgBox("Could not start Cs Api") Else Set apiHandler = obj End If

测试一切是否正常工作

将使用CallByName函数。CallByName()函数用于通过执行方法或设置属性值来操作对象。

Dim sRet As String sRet = CallByName(apiHandler, "SessionID", VbGet) Msgbox sRet

需要做什么

需要使用方法1,附加到一个C#事件,执行一个方法(MultiplyAndNotify),并从VB6获得回调。

Dim functinPointerAddress As Long functinPointerAddress = getAddressOfFunction(AddressOf Vb6NotifyEventOnModule) Dim o As Object Set o = Me ret = CallByName(apiHandler, "AttachToAnEventMethod1", VbMethod, functinPointerAddress) ret = CallByName(apiHandler, "MultiplyAndNotify", VbMethod, 3, 5) Dim oRet oRet = CallByName(apiHandler, "DetachMethod1", VbMethod) MsgBox ret
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485