VC++ .NET 动态加载DLL,使用反射方式Invoke委托调用
扫描二维码
随时随地手机看文章
因为我是做嵌入式开发的,每次设备程序更新后都需要修改上位机,并且多个上位机,修改起来特麻烦,又不想用C#主要是底层使用的是C语言,配置解析通信等在单片机里面写好后可以直接复制到C++中使用,比较方便,我使用VC++开发比较方便,但是资料少,因此折腾了几晚上.
每次新产品都需要配一个上位机,并且本地配置与远程配置都需要重新开放配置程序,因此就想办法把配置模块变为一个动态的控件,一次开发后续2个程序都可以同时使用,使用了很多种方法,最后还是使用反射方式.
一.首先新建一个窗体控件DLL
将需要的界面从源程序拷贝过来
//对外接口函数,所有参数均为Object ^类型
//输入的配置,用于设置输入配置的缓冲区 public:void Object_SetInConfig(Object ^Parameter) { if(Parameter == nullptr) { System::Windows::Forms::MessageBox::Show("内存不足!","错误", System::Windows::Forms::MessageBoxButtons::OK,System::Windows::Forms::MessageBoxIcon::Error); return; } SetInConfig((void*)Convert::ToInt32(Parameter)); //调用函数设置输入的配置参数 } //检查参数是否合法 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("内存不足!","错误", 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; }
二.动态加载调用
//通过方法名称获得方法 System::Reflection::Assembly ^assembly; System::Type^ type; Object ^obj; System::Reflection::MethodInfo ^XF_SetInConfig; //显示读取的配置 System::Reflection::MethodInfo ^XF_CheckParameter; //参数无误 System::Reflection::MethodInfo ^XF_SaveConfig; //存储配置 System::Reflection::MethodInfo ^XF_GetConfigSize; //获取配置大小 System::Reflection::MethodInfo ^XF_DefaultConfig; //加载默认
//动态加载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("找不到依赖的设备配置文件: "+this->pDevDLL+" 程序无法继续运行!","程序发生错误", 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"); //参数检查 this->XF_SaveConfig = this->type->GetMethod("Object_SaveConfig"); //存储配置 this->XF_GetConfigSize = this->type->GetMethod("Object_GetConfigSize"); //获取配置大小 this->XF_DefaultConfig = this->type->GetMethod("DefaultConfig"); //加载默认 }
//使用反射调用函数
1.无参数,无返回函数调用最简单
this->XF_DefaultConfig->Invoke(this->obj, nullptr);
2.带返回参数的函数调用,此处返回的是bool类型
if((bool)this->XF_CheckParameter->Invoke(this->obj, nullptr) == true)//检查参数 { System::Windows::Forms::MessageBox::Show("配置参数无误!","提示", System::Windows::Forms::MessageBoxButtons::OK,System::Windows::Forms::MessageBoxIcon::None); this->toolStripStatusLabel1->Text = "参数检查无误"; }
3.带参数的函数调用需要使用cli::array< Object ^>^
将指针转化为int类型传入到参数表d中,此处只有1个形参,因此为
cli::array< Object ^>(1),多个按照实际填写.
cli::array< Object ^>^ d = gcnew cli::array< Object ^>(1); d[0] = (int)&RTU_Config; //获取指针并转化为int this->XF_SaveConfig->Invoke(this->obj, d); //存储配置
三.本地配置上位机与其它程序实现统一,一次编写,2个地方均可以使用
本地配置程序加载的配置控件
远程后台加载的同样的控件
四.可实现同一个程序完成多个功能
同一个程序动态加载不同控件实现不同功能
五.通过与ini配置文件结合,可以在不修改程序代码的情况下增加新设备支持,类似于插件
[设备数量] NUM=4 [设备类型] TYPE0=XF-RTU(老版) TYPE1=XF-RTU-N(标准版) TYPE2=XF-RTU-N(双DTU版) TYPE3=XF-RTU-M(精简版) [设备说明] 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