1 動態庫的相互調用
1.1 C#調用C++ dll步驟(只能導出方法):
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”
另外,可以通過VS的異常窗口,取消掉對 PInvokeStackImbalance異常的檢測:
點擊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
#using "CSharpDllProject.dll" using namespace CSharpDllProject;
1.3 c#調用c++類步驟(c++/cli,可直接使用C++類)
c++/cli簡介:C++/CLI標准是基於Microsoft提交的標准C++與通用語言基礎結構(Common Language Infrastructure)結合的技術
1.使用c++/cli語法對標准c++類進行包裝(可采用聚合模式,引用標准c++類,實現所有標准c++的方法)
2.c#引用c++ dll后,可直接new出一個 c++/cli創建的托管類對象
2 DEMOS
2.1 BASIC
新建一個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項目:
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); // } }
動態加載、卸載
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()來得到
參考文章: