.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.Convert 把 int 转换成对应的 LightjamsSpoutLib.EPixelFormat
调用函数的参数2改为NePixelFormat,内部调用委托时,转换成int即可
public void ReceiveImage(System.Array bytes, NePixelFormat format)
{
_receiveImage.Invoke(bytes, (int)format);
}