C++封装C#中的事件

背景:

  用户要求我们提供给他们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; //泛型委托事件

}


b. 在添加一个类型:MyEventArgs,继承自:EventArgs,包含两个额外属性:

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控制台测试程序,将会看到:



说明已经封装成功了。


需要源码的同学可以点击这里下载


  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
在计算机编程,C和C++都是非常重要的编程语言。它们都是被广泛应用于系统开发和高性能应用程序的常用语言。 C是一种被广泛使用的、面向过程的编程语言。它由Dennis Ritchie在20世纪70年代初开发,一开始被用于UNIX操作系统的开发。C具有简洁的语法结构和丰富的函数库,使得它非常适合用于系统级编程和嵌入式设备开发。C还可以编写高效的代码,并具有较少的内存占用,因此它对于资源有限的设备非常合适。许多其他编程语言,如C++、Java和Python等,都是在C的基础上开发的。 C++是在C语言的基础上扩展而来的编程语言。它由Bjarne Stroustrup在20世纪80年代开发,旨在为C语言添加面向对象的特性。C++继承了C的简洁性和高效性,并添加了类、继承、封装和多态等面向对象的功能。C++还具有强大的标准模板库(STL),提供了许多现成的容器和算法,使得程序开发更加快速和简便。C++广泛应用于游戏开发、图形界面以及大规模软件系统等领域。 虽然C和C++在某些方面相似,但它们也有一些重要的区别。C语言更加简洁,具有更小的语法和更少的特性,因此更加适合编写较低级别的、对性能要求较高的程序。而C++则更加强大和灵活,具有更多的特性和功能,使得它更适合开发大型软件系统和应用程序。 总体而言,C和C++都是非常重要的编程语言。选择使用哪个取决于项目的具体需求和个人的偏好。无论是选择C还是C++,掌握这些编程语言的基本概念和语法结构都是非常有益的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值