.net下com調用支持x86/x64

原由

項目涉及u3d/wpf端的渲染圖形合成,採用了開源項目spout,爲了便捷,採用了spout的com版本做爲c#端的調用
項目調整後,細節已經捋清楚了。
可是考慮桌面應用採用anypc,根據運行環境自動切換x86/x64,就不想硬編碼綁定運行環境了。
故選項後採用 程序選擇anypc,運行時動態判斷x86/x64,加載對應的com組件
封裝代理層,代理層調用對應的x86/x64 com pinvokegit

封裝分析

項目 LightjamsSpout
咱們須要封裝的部分是github

GLContext和LightjamsSpoutReceiver
都是com接口,硬編碼直接實例化便可,動態調用時,不能實例化接口,須要經過反射拿到類型上面的特性 CoClassAttribute
經過獲取 CoClassAttribute.CoClass 拿到實際類型去實例化
將x86/x64的庫分別放在不一樣的路徑,經過判斷 IntPtr.Size == 4/!Environment.Is64BitProcess 判斷是不是x86,反之則爲x64
經過項目的引用,把com組件轉換成 Interop.xx.dll ,路徑爲項目文件/obj/Debug,項目動態加載其實是這個 Interop.xx.dllc#

代碼實現

代理端動態加載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對應的引用類型
畢竟定義是3d

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內部的自定義枚舉類型
根據內容,作一個枚舉類型code

public enum NePixelFormat
    {
        BGR,
        RGB
    }

表達式樹的參數2改成int,內部轉換 Expression.Convertint 轉換成對應的 LightjamsSpoutLib.EPixelFormat
調用函數的參數2改成NePixelFormat,內部調用委託時,轉換成int便可orm

public void ReceiveImage(System.Array bytes, NePixelFormat format)
        {
            _receiveImage.Invoke(bytes, (int)format);
        }
相關文章
相關標籤/搜索