背景:
用户要求我们提供给他们API,如果这些API都是用C#写的DLL,而用户要求API为C++的 DLL,这种情况下如果不重写API,就需要将C# DLL里面的内容封装成C++ DLL,再将C++DLL提供给他们。
这里针对c#中的事件,记录一下如何使用C++来封装c#的事件:
1.创建一个简单的C# DLL实例:
a.新建一个c# DLL项目,取名:TestDotNetDLL,添加一个类:Customer,这个类里面有两个属性、一个自定义委托和三个事件(将要对这三种事件进行封装):
public class Customer { private string _surname = "Default"; private int _age = 20; public string Surname { get { return _surname; } set { _surname = value; } } public int Age { get { return _age; } set { _age = value; } } public delegate void MyEventHandler(object sender, MyEventArgs e); //自定义委托 //三个事件 public event EventHandler OnMyTestEvent; //默认委托型事件 public event MyEventHandler OnMyTestEvent1; //自定义委托事件 public event EventHandler<MyEventArgs> OnMyTestEvent2; //泛型委托事件 }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestDotNetDLL { public class MyEventArgs : EventArgs { #region Properties private string _surname="XiaoHong"; private int _age=17; public string Surname { get { return _surname; } set { _surname = value; } } public int Age { get { return _age; } set { _age = value; } } #endregion } }
2.新建一个C++DLL空项目,取名:CplusplusDLLUsingDotNetDLL,用来封装刚刚新建的C# DLL
a.在项目属性中设置一下:Common Language Runtime Support (/clr)
b.在common properties中添加引用C# DLL:
c.添加两个头文件:CustomerWin32Cls.h,CustomerMiddle.h
CustomerWin32Cls.h:
#pragma once #include <string> using namespace std; typedef unsigned int uint; #ifdef CPLUSPLUSDLLUSINGDOTNETDLL_EXPORTS #include "CustomerMiddle.h" using namespace System; #define DLLSPEC __declspec( dllexport) #else class CustomerMiddle; #define DLLSPEC #endif namespace CustomerWrapper { class DLLSPEC CustomerWin32 { private: CustomerMiddle* m_pCustomer; public: CustomerWin32(); ~CustomerWin32(); //event __event void OnMyTestEvent(const char* obj, unsigned int e); void HandleOnMyTestEvent(const char* obj, unsigned int e); __event void OnMyTestEvent1(const char* obj, unsigned int e); void HandleOnMyTestEvent1(const char* obj, unsigned int e); __event void OnMyTestEvent2(const char* obj, unsigned int e); void HandleOnMyTestEvent2(const char* obj, unsigned int e); }; }CustomerMiddle.h:
#pragma once #include <vcclr.h> #include <msclr\marshal.h> #include <msclr\auto_gcroot.h> typedef unsigned int uint; using namespace System; namespace CustomerWrapper { public class CustomerMiddle { private: public: msclr::auto_gcroot<TestDotNetDLL::Customer^> obj; CustomerMiddle(); ~CustomerMiddle(); }; class CustomerWin32; // forward declaration ref class EventHelper { private: CustomerWin32* m_pCustomerWin32; public: EventHelper(CustomerWin32* pCustomerWin32); EventHelper(); ~EventHelper(); void OnMyTestEvent(Object^ obj, System::EventArgs^ e); void OnMyTestEvent1(Object^ obj, TestDotNetDLL::MyEventArgs^ e); void OnMyTestEvent2(Object^ obj, TestDotNetDLL::MyEventArgs^ e); }; }
d.添加对应的cpp源文件:
CustomerWin32Cls.cpp:
#include "CustomerWin32Cls.h" #include <string> #include <stdlib.h> #include <msclr\marshal.h> using namespace std; using namespace msclr::interop; using namespace System; using namespace CustomerWrapper; using namespace TestDotNetDLL; #define CLRSTRING(stdString) (gcnew String(stdString)) // stdString is a std string variable #define CUSTOMEROBJ m_pCustomer->obj CustomerWin32:: CustomerWin32() { m_pCustomer=new CustomerMiddle(); CUSTOMEROBJ= gcnew TestDotNetDLL::Customer(); EventHelper^ eventHelper = gcnew EventHelper( this); CUSTOMEROBJ->OnMyTestEvent+=gcnew System::EventHandler(eventHelper,&EventHelper::OnMyTestEvent); CUSTOMEROBJ->OnMyTestEvent1+=gcnew TestDotNetDLL::Customer::MyEventHandler(eventHelper,&EventHelper::OnMyTestEvent1); CUSTOMEROBJ->OnMyTestEvent2+=gcnew System::EventHandler<TestDotNetDLL::MyEventArgs^>(eventHelper,&EventHelper::OnMyTestEvent2); } CustomerWin32:: ~CustomerWin32() { delete m_pCustomer; } void CustomerWin32:: HandleOnMyTestEvent(const char* obj, unsigned int e) { __raise OnMyTestEvent( obj, e); } void CustomerWin32:: HandleOnMyTestEvent1(const char* obj, unsigned int e) { __raise OnMyTestEvent1( obj, e); } void CustomerWin32:: HandleOnMyTestEvent2(const char* obj, unsigned int e) { __raise OnMyTestEvent2( obj, e); }CustomerMiddle.cpp:
#include "CustomerWin32Cls.h" #include "CustomerMiddle.h" using namespace System; using namespace msclr::interop; using namespace CustomerWrapper; using namespace TestDotNetDLL; EventHelper:: EventHelper( CustomerWin32* pCustomerWin32) { m_pCustomerWin32 = pCustomerWin32; } EventHelper::EventHelper() { } EventHelper:: ~EventHelper() { } void EventHelper:: OnMyTestEvent( Object^ sender, System::EventArgs^ e) { m_pCustomerWin32->HandleOnMyTestEvent(0, 1); } void EventHelper:: OnMyTestEvent1( Object^ sender, TestDotNetDLL::MyEventArgs^ e) { String^ name = e->Surname; uint age = e->Age; marshal_context^ context = gcnew marshal_context(); const char * customerName = context->marshal_as<const char*>(name); m_pCustomerWin32->HandleOnMyTestEvent1( customerName, age); } void EventHelper:: OnMyTestEvent2( Object^ sender, TestDotNetDLL::MyEventArgs^ e) { String^ name = e->Surname; uint age = e->Age; marshal_context^ context = gcnew marshal_context(); const char * customerName = context->marshal_as<const char*>(name); m_pCustomerWin32->HandleOnMyTestEvent2( customerName, age); } CustomerMiddle::CustomerMiddle() { } CustomerMiddle::~CustomerMiddle() { }
3.新建c++ DLL测试项目:
a.新建一个C++ win32控制台项目(包含预编译头),取名:App_TestWin32DLL,同样在项目属性中设置一下支持:Common Language Runtime Support (/clr);
b.在【C/C++】->【General】->【Additional Include Directories】添加CustomerWin32Cls.h所在的路径(如果没有C/C++选项,随便新建一个cpp文件编译一下就出现了);
c.在【Linker】->【General】->【Additional Library Directories】添加CplusplusDLLUsingDotNetDLL.lib所在的路径;
d.在【Linker】->【Input】->【Additional Dependencies】添加CplusplusDLLUsingDotNetDLL.lib(包含后缀名);
e.添加一个测试头文件:TestWrapper.h
#include "stdafx.h" using namespace CustomerWrapper; typedef unsigned int uint; class TestWrapper{ private: CustomerWin32* customer; public : TestWrapper(); ~TestWrapper(); void OnMyTestEvent(const char* obj, uint e); void OnMyTestEvent1(const char* obj, uint e); void OnMyTestEvent2(const char* obj, uint e); };
f.在头文件stdafx.h中添加需要的头文件引用:
// stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #pragma once #include "targetver.h" #include <stdio.h> #include <tchar.h> #include <vcclr.h> // TODO: reference additional headers your program requires here #include "CustomerWin32Cls.h" #include "TestWrapper.h" #include <iostream>
g.添加头文件TestWrapper.h对应的源文件:
#include "stdafx.h" TestWrapper::TestWrapper() { customer=new CustomerWin32(); __hook( &CustomerWrapper::CustomerWin32::OnMyTestEvent, customer, &TestWrapper::OnMyTestEvent); __hook( &CustomerWrapper::CustomerWin32::OnMyTestEvent1, customer, &TestWrapper::OnMyTestEvent1); __hook( &CustomerWrapper::CustomerWin32::OnMyTestEvent2, customer, &TestWrapper::OnMyTestEvent2); } void TestWrapper::OnMyTestEvent(const char* obj,uint e) { System::Console::WriteLine("OnMyTestEvent : Hello"); } void TestWrapper::OnMyTestEvent1(const char* obj,uint e) { cout<<"OnMyTestEvent1 : name is: "<<obj<<" ,age is: "<<e<<endl; } void TestWrapper::OnMyTestEvent2(const char* obj,uint e) { cout<<"OnMyTestEvent2 : name is: "<<obj<<" ,age is: "<<e<<endl; } TestWrapper:: ~TestWrapper() { __unhook( &CustomerWrapper::CustomerWin32::OnMyTestEvent, customer, &TestWrapper::OnMyTestEvent); __unhook( &CustomerWrapper::CustomerWin32::OnMyTestEvent1, customer, &TestWrapper::OnMyTestEvent1); __unhook( &CustomerWrapper::CustomerWin32::OnMyTestEvent2, customer, &TestWrapper::OnMyTestEvent2); delete customer; }
h.最后在主函数中添加测试代码:
#pragma once #include "stdafx.h" using namespace CustomerWrapper; using namespace std; int _tmain(int argc, _TCHAR* argv[]) { TestWrapper wrapper; while (1) { int x; cin >> x; } return 0; }
注意,这样运行是没有任何结果的,因为事件并没有执行,需要在C# DLL中添加事件执行代码:
在Customer.cs文件中添加一个定时器,每隔1秒,执行一下事件:
public class Customer { private System.Timers.Timer Timer; public Customer() { Timer = new System.Timers.Timer(); Timer.Interval = 1000; Timer.Elapsed += new System.Timers.ElapsedEventHandler(Timer_Elapsed); Timer.AutoReset = true; Timer.Enabled = true; Timer.Start(); } void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (OnMyTestEvent != null) { OnMyTestEvent(sender,e); } if (OnMyTestEvent1 != null) { MyEventArgs me = new MyEventArgs(); OnMyTestEvent1(sender, me); } if (OnMyTestEvent2 != null) { MyEventArgs me = new MyEventArgs(); me.Surname = this.Surname; me.Age = this.Age; OnMyTestEvent2(sender, me); } } }
最后重新编译一下C# DLL,运行App_TestWin32DLL控制台测试程序,将会看到:
说明已经封装成功了。
需要源码的同学可以点击这里下载