实践:C++平台迁移以及如何用C#做C++包装层

发表于:2014-8-05 09:53

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:天天不在    来源:51Testing软件测试网采编

  如上定义一个宏定义,在导出的C++动态链接库中,可以选择项目属性里添加预处理器定义TRADITIONALDLL_EXPORTS,也或者是在引用这个文件加上.而在引用这个动态链接库不做处理.
  先看一个普通的函数从非托管C++到C++/CRL到C#相应流程,这个函数是传入一个设备ID,得到设备的所有工程,以及默认的工程ID.
1  // C++
2 TRADITIONALDLL_API int GetTestList(const unsigned long deviceId, char** confs, int& count, int& defaultID);
3 TRADITIONALDLL_API  int GetTestList(const unsigned long deviceId, char** confs, int& count, int& defaultID)
4 {
5      auto tests = PApi->Spider_GetTestList();
6      count = tests->count;
7      if (count > 0)
8      {
9           auto testNames = new char[32 * count];
10           memset(testNames, 0, 32 * count);
11           for (int i = 0; i < count; i++)
12           {
13                memcpy(testNames + i * 32, tests->driveArray[i].TestName, 32);
14           }
15           *confs = testNames;
16           defaultID = PApi->curModule->nDefaultID;
17           return FUNC_SUCCESS;
18      }
19      spiderAPI->errorStr = NotConnecteDev;
20      return 0;
21 }
22 // managed C++
23 bool GetTestList(const unsigned long deviceId, [Out]List <String^>^% testList, [Out]int% defalutID);
24 bool DeviceController::GetTestList(const unsigned long deviceId, [Out]List <String^>^% testList, [Out]int% defalutID)
25 {
26      testList = gcnew List<String^>();
27      char *nameBuffer = NULL;
28      int testCount = 0;
29      int dID = 0;
30      ::GetTestList(deviceId, &nameBuffer, testCount, dID);
31      defalutID = dID;
32      if (nameBuffer != NULL && testCount > 0)
33      {
34           char testName[32];
35           memset(testName, 0, 32);
36           for (int index = 0; index < testCount; ++index)
37           {
38                memcpy(testName, nameBuffer + index * 32, 32);
39                String^ str = gcnew String(testName);
40                testList->Add(str);
41           }
42           return true;
43      }
44      return false;
45 }
46 //C#
47 bool result = DeviceController.Instance.GetTestList(Device.Id, out testNames, out defaultID);
  基本的传递如上,但是现在要求C#实时刷新设备转过来的数据,简单来说,就是C++里socket接收线程收到设备发送的数据,需要通知C#界面刷新.看需求,C#里的事件就能满足,但是是C++发送的消息,在这我们根据C++里的回调函数与托管代码里的事件结合来完成,去掉一些不必要的代码,主要过程如下.
1 // C++
2 typedef void (__stdcall *OnDataMessageRev)(const unsigned long deviceId,  char* data, const int eventId,const int p0, const int p1,const int p2);
3
4 class Module
5 {
6      OnDataMessageRev onDataRev;
7      void didDataReceived();
8      void SetDataMessageCallback(OnDataMessageRev callback);
9 }
10 void Module::SetDataMessageCallback(OnDataMessageRev callback)
11 {
12      onDataRev = callback;
13 }
14 void Module::didDataReceived()
15 {
16     switch (dataMsg.Msg.nEventID)
17     {
18         case DSP_DISPNEXT_OK:
19         {
20              if (onDataRev)
21                   onDataRev(this->deviceId, dataMsg.Data, dataMsg.Msg.nEventID, dataMsg.Msg.nParameters0, dataMsg.Msg.nParameters1, dataMsg.Msg.nParameters2);
22          }
23         break;
24         //...
25     }
26 }
27 DEVICEAPI_API void SetDataMessageCallback(OnDataMessageRev callback);
28 DEVICEAPI_API void SetDataMessageCallback(OnDataMessageRev callback)
29 {
30      model.SetDataMessageCallback(callback);
31 }
32 // managed C++
33 public delegate void DeviceDataMessageHandler(const unsigned long deviceId, const array<Byte>^ data, const int eventId, const int p0, const int p1, const int p2);
34 public delegate void DeviceDataCallback(const unsigned long deviceId, char* data, const int eventId, const int p0, const int p1, const int p2);
35 public ref class DeviceController
36 {
37     DeviceDataCallback^ dataCallback;
38     DeviceDataMessageHandler^ onDeviceDataReceived;
39     event DeviceDataMessageHandler^ DeviceDataReceived
40      {
41           void add(DeviceDataMessageHandler^ h)
42           {
43                onDeviceDataReceived += h;
44           }
45           void remove(DeviceDataMessageHandler^ h)
46           {
47                onDeviceDataReceived -= h;
48           }
49      }
50
51     DeviceController::DeviceController()
52      {
53           dataCallback = gcnew DeviceDataCallback(&(DeviceController::DataReceivedCallback));
54           IntPtr ptrData = Marshal::GetFunctionPointerForDelegate(dataCallback);
55
56           ::SetDataMessageCallback(static_cast<OnDataMessageRev>(ptrData.ToPointer()));
57           GC::KeepAlive(dataCallback);
58      }
59
60 void OnDeviceDataReceived(const unsigned long deviceId, const array<Byte>^ data, const int eventId, const int p0, const int p1, const int p2)
61 {
62      DeviceDataMessageHandler^ handler = onDeviceDataReceived;
63      if (handler != nullptr)
64      {
65           handler(deviceId, data, eventId, p0, p1, p2);
66      }
67 }
68 }
69
70 //C#
71
72 DeviceController.Instance.DeviceDataReceived += Instance_DeviceDataReceived;
73
74 T ByteArrayToStructure<T>(byte[] bytes, IntPtr pin, int offset) where T : struct
75 {
76     try
77     {
78         return (T)Marshal.PtrToStructure(pin + offset, typeof(T));
79     }
80     catch (Exception e)
81     {
82         return default(T);
83     }
84 }
85 private void Instance_DeviceDataReceived(uint deviceId, byte[] data, int eventId, int p0, int p1, int p2)
86 {
87      GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
88         IntPtr pin = handle.AddrOfPinnedObject();
89      int nCheckNum = ByteArrayToStructure<int>(data, pin, offset);
90      DISPLAYPARAMS displayParams = ByteArrayToStructure<DISPLAYPARAMS>(data, pin, offset);
91      VCSParamsDSP vcsPar = ByteArrayToStructure<VCSParamsDSP>(data, pin, offset);
92      handle.Free();
93
94 }
  C++里的memcyp确实很好用,上段代码中,ByteArrayToStructure也能实现如memcyp一样的功能,先用GCHandle.Alloc选择Pinned生成CG不能回改的内存区域,就和C++申请内存一样,然后根据偏移量offset,把对应的字节转成我们需要的数据.C++里的char和C#里的byte是一样的,都是一个字节,这里不要搞错了,也和C++一样,记的清除申请的内存空间.
22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号