1.1 C#調用C++ dll步驟(只能導出方法):c++
1. c++創建空項目->源文件文件夾中添加cpp文件和函數
2. c++屬性設置中,配置類型設置爲動態庫dll,公共語言運行時支持改成/clr
3. c#引用c++的dll
4. c#聲明c++的方法,並添加 DllImport特性
5. c#工程屬性設置爲:目標平臺x86
6. 注意方法的類型匹配
7. 引起PInvokeStackImbalance異常:注意:C++的"_declspec"和C#的「CallingConvention=CallingConvention.Cdecl」c#
另外,能夠經過VS的異常窗口,取消掉對 PInvokeStackImbalance異常的檢測:ide
點擊VS的「調試 - 異常」,打開異常窗口,展開選擇「Managed Debugging Assistants\PInvokeStackImbalance」,去掉對應的「引起」可選框。
1.2 c++調用 c# dll的步驟(可直接使用C#類):函數
1. 建立c++控制檯應用程序
2. 拷貝c# dll到c++工程根目錄
3. 工程屬性->配置->常規->公共語言運行時支持->clr
工程屬性->配置->c/c++常規->調試信息格式->zi
工程屬性->配置->c/c++常規->公共語言運行時支持->clr
spa
#using "CSharpDllProject.dll" using namespace CSharpDllProject;
1.3 c#調用c++類步驟(c++/cli,可直接使用C++類).net
c++/cli簡介:C++/CLI標準是基於Microsoft提交的標準C++與通用語言基礎結構(Common Language Infrastructure)結合的技術
1.使用c++/cli語法對標準c++類進行包裝(可採用聚合模式,引用標準c++類,實現全部標準c++的方法)
2.c#引用c++ dll後,可直接new出一個 c++/cli建立的託管類對象線程
新建一個classlibrary,包含兩個類class1和class2,這兩個類中分別有一個方法,都是返回一個字符串,代碼以下:調試
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace mydll { public class Class1 { public Class1() { } public string sayhello() { return "hello,word!"; } } public class Class2 { public Class2() { } public string saybeautiful() { return "beautiful,very good!"; } } }
在編譯完成後會生成一個mydll.dll動態連接庫,而後新建一個winform項目:code
private void button1_Click(object sender, EventArgs e) { string path = @"D:\123\mydll\mydll\bin\Debug\mydll.dll"; //Byte[] byte1 = System.IO.File.ReadAllBytes(path);//也是能夠的 //Assembly assem = Assembly.Load(byte1); Assembly assem = Assembly.LoadFile(path); //string t_class = "mydll.Class1";//理論上已經加載了dll文件,能夠經過命名空間加上類名獲取類的類型,這裏應該修改成以下: //string t_class = "mydll.Class1,mydll";//若是你想要獲得的是被本工程內部的類,能夠「命名空間.父類……類名」;若是是外部的,須要在後面加上「,連接庫名」; //再次感謝thy38的幫助。 //Type ty = Type.GetType(t_class);//這兒在調試的時候ty=null,一直不理解,望有高人能夠解惑 Type[] tys = assem.GetTypes();//只好獲得全部的類型名,而後遍歷,經過類型名字來區別了 foreach (Type ty in tys)//huoquleiming { if (ty.Name == "Class1") { ConstructorInfo magicConstructor = ty.GetConstructor(Type.EmptyTypes);//獲取不帶參數的構造函數 object magicClassObject = magicConstructor.Invoke(new object[] { });//這裏是獲取一個相似於類的實例的東東 //object magicClassObject = Activator.CreateInstance(t);//獲取無參數的構造實例還能夠經過這樣 MethodInfo mi = ty.GetMethod("sayhello"); object aa=mi.Invoke(magicClassObject, null); MessageBox.Show(aa.ToString());//這兒是執行類class1的sayhello方法 } if (ty.Name == "Class2") { ConstructorInfo magicConstructor = ty.GetConstructor(Type.EmptyTypes);//獲取不帶參數的構造函數,若是有構造函數且沒有不帶參數的構造函數時,這兒就不能這樣子啦 object magicClassObject = magicConstructor.Invoke(new object[] { }); MethodInfo mi = ty.GetMethod("saybeautiful"); object aa = mi.Invoke(magicClassObject, null);//方法有參數時,須要把null替換爲參數的集合 MessageBox.Show(aa.ToString()); } } //AppDomain pluginDomain = (pluginInstanceContainer[key] as PluginEntity).PluginDomain; //if (pluginDomain != null) //{ // AppDomain.Unload(pluginDomain); // } }
動態加載、卸載orm
c#中經過反射能夠方便的動態加載dll程序集,可是若是你須要對dll進行更新,卻發現.net類庫沒有提供卸載dll程序集的方法。在.net 中,加入了應用程序域的概念,應用程序域是能夠卸載的。也就是說,若是須要對動態加載的dll程序集進行更新,能夠經過如下方法解決:
新建一個應用程序域,在該應用程序域中動態加載DLL,而後能夠卸載掉該應用程序域。該應用程序域被卸載的時候,相關資源也會被回收。
要想這樣實現,就要讓你程序的currentDomain和新建的newDomain之間進行通訊,穿過應用程序域的邊界。從網上找到了某大牛的解決方法,抄下來留給本身看吧:
using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Reflection; namespace UnloadDll { class Program { static void Main(string[] args) { string callingDomainName = AppDomain.CurrentDomain.FriendlyName;//Thread.GetDomain().FriendlyName; Console.WriteLine(callingDomainName); AppDomain ad = AppDomain.CreateDomain("DLL Unload test"); ProxyObject obj = (ProxyObject)ad.CreateInstanceFromAndUnwrap(@"UnloadDll.exe", "UnloadDll.ProxyObject"); obj.LoadAssembly(); obj.Invoke("TestDll.Class1", "Test", "It's a test"); AppDomain.Unload(ad); obj = null; Console.ReadLine(); } } class ProxyObject : MarshalByRefObject { Assembly assembly = null; public void LoadAssembly() { assembly = Assembly.LoadFile(@"TestDLL.dll"); } public bool Invoke(string fullClassName, string methodName, params Object[] args) { if(assembly == null) return false; Type tp = assembly.GetType(fullClassName); if (tp == null) return false; MethodInfo method = tp.GetMethod(methodName); if (method == null) return false; Object obj = Activator.CreateInstance(tp); method.Invoke(obj, args); return true; } } }
注意:
1. 要想讓一個對象可以穿過AppDomain邊界,必需要繼承MarshalByRefObject類,不然沒法被其餘AppDomain使用。
2. 每一個線程都有一個默認的AppDomain,能夠經過Thread.GetDomain()來獲得
參考文章: