起因
專案涉及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);
}