VC++ .NET 動態(tài)加載DLL,使用反射方式Invoke委托調(diào)用
因為我是做嵌入式開發(fā)的,每次設(shè)備程序更新后都需要修改上位機,并且多個上位機,修改起來特麻煩,又不想用C#主要是底層使用的是C語言,配置解析通信等在單片機里面寫好后可以直接復(fù)制到C++中使用,比較方便,我使用VC++開發(fā)比較方便,但是資料少,因此折騰了幾晚上.
每次新產(chǎn)品都需要配一個上位機,并且本地配置與遠程配置都需要重新開放配置程序,因此就想辦法把配置模塊變?yōu)橐粋€動態(tài)的控件,一次開發(fā)后續(xù)2個程序都可以同時使用,使用了很多種方法,最后還是使用反射方式.
一.首先新建一個窗體控件DLL
將需要的界面從源程序拷貝過來
//對外接口函數(shù),所有參數(shù)均為Object ^類型
//輸入的配置,用于設(shè)置輸入配置的緩沖區(qū) public:void?Object_SetInConfig(Object?^Parameter) { if(Parameter?==?nullptr) { System::Windows::Forms::MessageBox::Show("內(nèi)存不足!","錯誤", System::Windows::Forms::MessageBoxButtons::OK,System::Windows::Forms::MessageBoxIcon::Error); return; } SetInConfig((void*)Convert::ToInt32(Parameter)); //調(diào)用函數(shù)設(shè)置輸入的配置參數(shù) } ???//檢查參數(shù)是否合法 public:Object?^Object_XF_CheckParameter(void) ???{ ???Object?^temp?=?gcnew?Object; ???temp?=?this->CheckParameter(); ???return?temp; ???} ???//存儲配置 public:Object?^Object_SaveConfig(Object?^Parameter) ???{ ???Object?^temp?=?gcnew?Object; ???if(Parameter?==?nullptr) ???{ ???System::Windows::Forms::MessageBox::Show("內(nèi)存不足!","錯誤", ???System::Windows::Forms::MessageBoxButtons::OK,System::Windows::Forms::MessageBoxIcon::Error); ???temp?=?false; ???return?temp; ???} ???temp?=?this->SaveConfig((void*)Convert::ToInt32(Parameter)); ???return?temp; ???} ???//獲取配置文件大小 public:Object?^Object_GetConfigSize(void) ???{ ???Object?^temp?=?gcnew?Object; ???temp?=?this->GetConfigSize(); ???return?temp; ???}
二.動態(tài)加載調(diào)用
//通過方法名稱獲得方法 System::Reflection::Assembly?^assembly; System::Type^?type; Object?^obj; System::Reflection::MethodInfo?^XF_SetInConfig; //顯示讀取的配置 System::Reflection::MethodInfo?^XF_CheckParameter; //參數(shù)無誤 System::Reflection::MethodInfo?^XF_SaveConfig; //存儲配置 System::Reflection::MethodInfo?^XF_GetConfigSize; //獲取配置大小 System::Reflection::MethodInfo?^XF_DefaultConfig; //加載默認
//動態(tài)加載DLL文件 void?LoadDLL(String?^pDLL) { try { this->assembly?=?System::Reflection::Assembly::LoadFrom(USER_LIB.GetRunningDirectory()+"\device\"+this->pDevDLL); //加載DLL } catch?(System::IO::FileNotFoundException^?e) { System::Windows::Forms::MessageBox::Show("找不到依賴的設(shè)備配置文件:?"+this->pDevDLL+"?程序無法繼續(xù)運行!","程序發(fā)生錯誤", System::Windows::Forms::MessageBoxButtons::OK,System::Windows::Forms::MessageBoxIcon::Error); Application::Exit(); //程序退出 return; } this->type?=?this->assembly->GetType("XF_RTU_N_V1_0_CONFIG.XF_RTU_N_V1_0_CONFIGControl"); this->obj?=?this->assembly->CreateInstance("XF_RTU_N_V1_0_CONFIG.XF_RTU_N_V1_0_CONFIGControl"); this->panel1->Controls->Add((System::Windows::Forms::Control?^)this->obj); //將DLL的控件加載到panel1并顯示 this->PerformLayout(); //通過方法名稱獲得方法 this->XF_SetInConfig?=?this->type->GetMethod("Object_SetInConfig"); //顯示讀取的配置 this->XF_CheckParameter?=?this->type->GetMethod("Object_XF_CheckParameter"); //參數(shù)檢查 this->XF_SaveConfig?=?this->type->GetMethod("Object_SaveConfig"); //存儲配置 this->XF_GetConfigSize?=?this->type->GetMethod("Object_GetConfigSize"); //獲取配置大小 this->XF_DefaultConfig?=?this->type->GetMethod("DefaultConfig"); //加載默認 }
//使用反射調(diào)用函數(shù)
1.無參數(shù),無返回函數(shù)調(diào)用最簡單
this->XF_DefaultConfig->Invoke(this->obj,?nullptr);
2.帶返回參數(shù)的函數(shù)調(diào)用,此處返回的是bool類型
if((bool)this->XF_CheckParameter->Invoke(this->obj,?nullptr)?==?true)//檢查參數(shù) ?{ ?System::Windows::Forms::MessageBox::Show("配置參數(shù)無誤!","提示", ?System::Windows::Forms::MessageBoxButtons::OK,System::Windows::Forms::MessageBoxIcon::None); ?this->toolStripStatusLabel1->Text?=?"參數(shù)檢查無誤"; ?}
3.帶參數(shù)的函數(shù)調(diào)用需要使用cli::array< Object ^>^
將指針轉(zhuǎn)化為int類型傳入到參數(shù)表d中,此處只有1個形參,因此為
cli::array<?Object?^>(1),多個按照實際填寫.
cli::array<?Object?^>^??d?=?gcnew?cli::array<?Object?^>(1); ?d[0]?=?(int)&RTU_Config; //獲取指針并轉(zhuǎn)化為int ?this->XF_SaveConfig->Invoke(this->obj,?d); //存儲配置
三.本地配置上位機與其它程序?qū)崿F(xiàn)統(tǒng)一,一次編寫,2個地方均可以使用
本地配置程序加載的配置控件
遠程后臺加載的同樣的控件
四.可實現(xiàn)同一個程序完成多個功能
同一個程序動態(tài)加載不同控件實現(xiàn)不同功能
五.通過與ini配置文件結(jié)合,可以在不修改程序代碼的情況下增加新設(shè)備支持,類似于插件
[設(shè)備數(shù)量] NUM=4 [設(shè)備類型] TYPE0=XF-RTU(老版) TYPE1=XF-RTU-N(標(biāo)準(zhǔn)版) TYPE2=XF-RTU-N(雙DTU版) TYPE3=XF-RTU-M(精簡版) [設(shè)備說明] INF0=第一代RTU INF1=第二代低功耗RTU INF2=第二代低功耗RTU INF3=低功耗簡版RTU口 [配置控件] DLL0=XF_RTU_老版本.dll DLL1=XF_RTU_N_V1_0_CONFIG.dll DLL2=XF_RTU_N_V1_0_CONFIG.dll DLL3=XF_RTU_N_V1_0_CONFIG.dll