.net下com调用支持x86/x64

起因

项目涉及u3d/wpf端的渲染图形合成,采用了开源项目spout,为了便捷,采用了spout的com版本作为c#端的调用
项目调整后,细节已经捋清楚了。
但是考虑桌面应用采用anypc,根据运行环境自动切换x86/x64,就不想硬编码绑定运行环境了。
故选项后采用 程序选择anypc,运行时动态判断x86/x64,加载对应的com组件
封装代理层,代理层调用对应的x86/x64 com pinvoke

封装分析

项目 LightjamsSpout
我们需要封装的部分是

GLContext和LightjamsSpoutReceiver
都是com接口,硬编码直接实例化即可,动态调用时,不能实例化接口,需要通过反射拿到类型上面的特性 CoClassAttribute
通过获取 CoClassAttribute.CoClass 拿到实际类型去实例化
将x86/x64的库分别放在不同的路径,通过判断 IntPtr.Size == 4/!Environment.Is64BitProcess 判断是否是x86,反之则为x64
通过项目的引用,把com组件转换成 Interop.xx.dll ,路径为项目文件/obj/Debug,项目动态加载实际上是这个 Interop.xx.dll

代码实现

代理端动态加载Assmebly

            loadDirectory = Path.Combine(Directory.GetCurrentDirectory(), IntPtr.Size == 4 ? "x86_Spout" : "x64_Spout");//LightjamsSpoutPS.dll
            var loadFile = Path.Combine(loadDirectory, "Interop.LightjamsSpoutLib.dll");

            assembly = Assembly.LoadFile(loadFile);

            var type = assembly.GetType("LightjamsSpoutLib.LightjamsSpoutReceiver");
            var coClassAttribute = type.GetCustomAttribute<CoClassAttribute>();

            try
            {
                instance = Activator.CreateInstance(coClassAttribute.CoClass);
            }
            catch (Exception e)
            {
                RegisterCom(loadDirectory);
                instance = Activator.CreateInstance(coClassAttribute.CoClass);
            }

通过表达式树构建实例的方法
比如LightjamsSpoutReceiver.Connect

        delegate string ConnectInvoke(string senderName, out int width, out int height);

        private ConnectInvoke CreateConnect()
        {
            var senderNameParam = Expression.Parameter(typeof(string), "senderName");
            var widthParam = Expression.Parameter(typeof(int).MakeByRefType(), "width");
            var heightParm = Expression.Parameter(typeof(int).MakeByRefType(), "height");

            var instanceExp = Expression.Constant(instance);
            var method = instance.GetType().GetMethod("Connect");

            var callExp = Expression.Call(instanceExp, method, new Expression[]{ senderNameParam, widthParam, heightParm });

            return Expression.Lambda<ConnectInvoke>(callExp, new ParameterExpression[]{ senderNameParam, widthParam, heightParm }).Compile();
        }

值得一提的是type.MakeByRefType是生成type对应的引用类型
毕竟定义是

public virtual string Connect(string senderName, out int width, out int height)

类型转换

有一个函数ReceiveImage,参数调用了com内部类型

public virtual void ReceiveImage(System.Array bytes, LightjamsSpoutLib.EPixelFormat format)

LightjamsSpoutLib.EPixelFormat是com内部的自定义枚举类型
根据内容,做一个枚举类型

    public enum NePixelFormat
    {
        BGR,
        RGB
    }

表达式树的参数2改为int,内部转换 Expression.Convertint 转换成对应的 LightjamsSpoutLib.EPixelFormat
调用函数的参数2改为NePixelFormat,内部调用委托时,转换成int即可

        public void ReceiveImage(System.Array bytes, NePixelFormat format)
        {
            _receiveImage.Invoke(bytes, (int)format);
        }
posted @ 2020-12-22 14:27  沉迷代码的萌新  阅读(789)  评论(0编辑  收藏  举报