任意C#的DLL不用注册实现被VB6调用

VB6与C#之间的交互,据我所知,有以下几种方式:
1.socket通讯方式,VB6有Winsocket控件,C#有专门的位于System.Net.Sockets下的通讯类
2.共享内存,共享内存C#有专门的封装类,详情见我的这篇博客
,但是VB操作共享内存麻烦,要调用Windows API(Kernel32.dll中的函数)
3.读写文件/数据库的方式。这种方式无非是轮询,一个程序写请求,另一个程序轮询读取请求然后处理请求的方式,效率较低,且开销较大
4.COM组件实现直接调用:将要调用的C#DLL写成COM组件,然后注册,然后在VB6中引用注册生成的tlb文件,但是有时候别人给我们调用的DLL不一定是COM组件的方式,且注册麻烦
5.COM组件间接调用,意思是写一个万能的C#的DLL,注册一次这个万能的DLL,这个万能C#的DLL可以调用任何其他C#程序集的任何公开的静态、成员方法.实现VB6间接调用任何其他的C#的DLL而不用注册这些C#的DLL
本文想着重介绍最后一种方式。
最后一种方式的调用流程如下:
在这里插入图片描述
本篇文章资源总结:
VB6 DLL/OCX,CSInterface一键注册工具
CSInterface源码
VB6解析JSON库

1.CSInterface
CSInterface里有两个方法,可以调用任意的其他C# DLL中的任意公开的静态方法和成员方法,代码放在了Github,传入其他C#的程序集路径或者名称,要调用的方法名称,方法参数,使用反射的方式调用,将实现代码贴出来,很简单:

		/// <summary>
        /// 加载程序集,assemblyName可以是程序集名称,可以是相对路径、绝对路径(相对于CSInterface的路径)
        /// </summary>
        /// <param name="assemblyName">可以是相对路径、绝对路径</param>
        /// <returns></returns>
        private Assembly LoadAssembly(string assemblyName)
        {
            Assembly asm = null;
            if (assemblyName.Contains(".dll"))
            {
                //说明是路径
                if (!Path.IsPathRooted(assemblyName))
                {
                    assemblyName = Path.Combine(GetAppDirectory(), assemblyName);
                }
                asm = Assembly.LoadFrom(assemblyName);
            }
            else
            {
                asm = Assembly.Load(assemblyName);
            }
            return asm;
        }
        public object InvokeMemberMethod(string assemblyName, string methodName, bool hasParas,object[] paras)
        {
            Assembly asm = LoadAssembly(assemblyName);
            foreach (var type in asm.GetTypes())
            {
                if (!type.IsClass)
                    continue;
                //获取类下面的方法
                var method = type.GetMethod(methodName);
                if (method != null)
                {
                    var obj = Activator.CreateInstance(type);
                    if (!hasParas)
                    {
                        return method.Invoke(obj,null);
                    }
                    else 
                    {
                        return method.Invoke(obj, paras);
                    }
                }
            }
            return null;
        }
        public object InvokeStaticMethod(string assemblyName, string methodName,bool hasParas, object[] paras)
        {
            Assembly asm = LoadAssembly(assemblyName);
            
            foreach (var type in asm.GetTypes())
            {
                if (!type.IsClass)
                    continue;
                //获取类下面的方法
                var method = type.GetMethod(methodName, BindingFlags.InvokeMethod|BindingFlags.Public| BindingFlags.Static);
                if (method != null)
                {
                    if (!hasParas)
                    {
                        return method.Invoke(null,null);
                    }
                    else 
                    {
                        return method.Invoke(null, paras);
                    }
                }
            }
            return null;
        }
        public object CreateInstance(string assemblyName, string className,bool hasParas,object[] paras)
        {
            Assembly asm = LoadAssembly(assemblyName);
            foreach (var type in asm.GetTypes())
            {
                if (!type.IsClass)
                    continue;
                //获取类下面的方法
                if (type.Name == className || type.FullName == className)
                {
                    if (hasParas)
                    {
                        return Activator.CreateInstance(type, paras);
                    }
                    else
                    {
                        return Activator.CreateInstance(type);
                    }
                }
            }
            return null;
        }
        public string GetAppDirectory()
        {
            return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        }

以上,至于为什么InvokeStaticMethod和InvokeMemberMethod方法要带一个hasParas的参数,不能直接给paras=null吗?因为这两个方法是给VB6调用的,在VB6中,最后一个参数设置为Variant的数组,不能等于空传过来,等于空传过来会报错,暂时还没有发现可以替代的方案。
CSInterface要写成COM组件的方式才能注册,关于如何将C#程序集写成COM组件,见参考文章
CSInterface注册,自己写了个工具,可以一键注册CSInterface和VB6的OCX,不用手去敲命令了,注册工具的代码代码见Github,截图如下:
在这里插入图片描述
用红线框起来的部分就是注册和卸载CSInterface.DLL用的,注册之后,会在程序目录下生成一个tlb文件,如下图所示:
在这里插入图片描述
2.编写一个供VB6调用的任意的DLL
任意的DLL中包含一个静态方法和非静态方法,代码如下:

namespace AnyCSharpDLL
{
    public class Class1
    {
        public static string GetString()
        {
            return "this is a string from C#";
        }
        public int Add(int a, int b)
        {
            return a + b;
        }
    }
}

将这个DLL(AnyCSharpDLL.dll)编译之后放在CSInterface.dll目录下,如下所示:
在这里插入图片描述
打开VB6,新建一个VB6的窗体工程,如下:
在这里插入图片描述
引用CSInterface注册之后的tlb文件,打开Project->Reference中,如果注册了,可以找到CSInterface,如下图所示:
在这里插入图片描述
将这个勾上
注意:如果注册了之后在Reference中找不到CSInterface,则可能要点击Brower按钮,去找到这个CSInterface.tlb文件,然后就能在Reference中显示了
然后开始编写VB的代码了:
首先在界面上添加两个按钮,分别用来测试两个方法:
在这里插入图片描述
编写代码如下:

Dim csWrapper As New CSInterface.csWrapper '实例化一个CSInterface.csWrapper的实例
Private Sub btnGetString_Click()
    '定义用于接收结果的字符串
    Dim result As String
    '定义传入参数,虽然GetString函数不需要传入参数,
    '但是InvokeStaticMethod需要传入Object[] 类型的参数,且VB中不能给空,否则会报错
    
    '因为GetString没有参数,所以随便定义包含一个元素的Variant数组传过去就好了
    Dim paras(0) As Variant
    '调用AnyCSharpDLL.dll中的GetString方法,第三个参数给False表示GetString不需要参数
    result = csWrapper.InvokeStaticMethod("AnyCSharpDLL.dll", "GetString", False, paras)
    MsgBox result
End Sub

Private Sub btnTestAdd_Click()
    Dim result As Integer
    Dim paras(0 To 1) As Variant
    paras(0) = 5 '第一个参数
    paras(1) = 6 '第二个参数
    '第三个参数给True表示AnyCSharpDLL.dll中的成员方法Add需要参数
    result = csWrapper.InvokeMemberMethod("AnyCSharpDLL.dll", "Add", True, paras)
    MsgBox result
End Sub

在这里插入图片描述
点击第一个按钮:
在这里插入图片描述
点击第二个按钮:
在这里插入图片描述
有点遗憾的是写csWrapper点的时候没有任何提示,跟写动态脚本一样,方法有几个参数以及参数的类型需要自己心知肚明,不然容易出错
使用注意事项:
C#返回给VB的必须是常用的数据类型,比如int,string等,如果是复杂的数据类型,建议在C#中序列化成JSON字符串返回给VB,然后在VB中解析JSON,附VB6解析JSON的库

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页