实现方法
本文采用C++ clr作为桥梁,将C++的对象与C#对象可以一一对应,并可以在C#中方便的使用。所以需要在解决方案中再额外添加一个C++ clr的项目。
以下是整个代码结构
Managed引用Native项目,而C#则是很方便的引用Manager。
Native项目
该项目创建两个类,Machine与MachineStatus。其中Machine中有一个属性是MachineStatus的一个实例。而MachineStatus中又含有两个属性。我们就通过简单的类来了解如何将C++的对象传递给C#的。以下是两个对象的头文件。当然定义了NATIVE_API,用于导出这两个类。
#ifdef NATIVE_EXPORTS
#define NATIVE_API __declspec(dllexport)
#else
#define NATIVE_API __declspec(dllimport)
#endif
#include "MachineStatus.h"
#include <iostream>
namespace native
{
class NATIVE_API CMachine
{
private:
CMachineStatus m_MachineStatus;
public:
std::string Message;
CMachine(void);
~CMachine();
CMachineStatus& GetStatus();
void SetStatus(CMachineStatus& value);
void Increase();
void Run();
void Pause();
void Stop();
};
}
#ifdef NATIVE_EXPORTS
#define NATIVE_API __declspec(dllexport)
#else
#define NATIVE_API __declspec(dllimport)
#endif
namespace native
{
class NATIVE_API CMachineStatus
{
private:
long iWorkStatus = 0;
long iBondNumber = 0;
public:
CMachineStatus();
~CMachineStatus();
long GetWorkStatus();
void SetWorkStatus(long value);
long GetBondNumber();
void SetBondNumber(long value);
};
}
Managed项目
该项目主要是将C++对象转换成托管类型。首先记得先引用Native项目。然后使用C++ clr的语法对托管对象实例化,主要是取出Native对象的地址,使用其地址创建。
#ifndef _NAME_H
#define _NAME_H
#include "../Native/Machine.h"
#include "MachineStatus.h"
#endif
using namespace System;
using namespace System::Text;
namespace Managed {
public ref class Machine
{
private:
native::CMachine* m_instance;
public:
property String^ Message {String^ get() { return gcnew String(m_instance->Message.c_str()); }};
property MachineStatus^ Status;
Machine()
{
m_instance = new native::CMachine();
Status = gcnew MachineStatus(&(m_instance->GetStatus()));
}
~Machine()
{
delete m_instance;
}
void Run()
{
return m_instance->Run();
}
void Pause()
{
return m_instance->Pause();
}
void Stop()
{
return m_instance->Stop();
}
void Increase()
{
return m_instance->Increase();
}
};
}
#ifndef _NAME_H
#define _NAME_H
#include "../Native/MachineStatus.h"
#endif
namespace Managed
{
public ref class MachineStatus
{
private:
native::CMachineStatus* native_Instance;
public:
MachineStatus()
{
native_Instance = new native::CMachineStatus();
}
MachineStatus(native::CMachineStatus* status)
{
native_Instance = status;
}
~MachineStatus()
{
delete(native_Instance);
}
property int WorkStatus
{
public:
int get()
{
return native_Instance->GetWorkStatus();
}
void set(int value)
{
native_Instance->SetWorkStatus(value);
}
}
property int BondNumber
{
public:
int get()
{
return native_Instance->GetBondNumber();
}
void set(int value)
{
native_Instance->SetBondNumber(value);
}
}
};
}
WpfApp项目
当托管对象创建后,C#便可以很轻松的使用。所以我创建了一个绑定示例看能不能正常工作
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800"
FontFamily="Consolas"
DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">
<StackPanel>
<Button Content="生产加1"
Click="BtnAdd_Click" />
<Button Content="启动"
Click="BtnStart_Click" />
<Button Content="暂停"
Click="BtnPause_Click" />
<Button Content="停止"
Click="BtnStop_Click" />
<StackPanel Orientation="Horizontal"
Margin="30,20,0,0">
<TextBlock Text="机器状态" />
<TextBlock Margin="10,0,50,0"
Name="_txbMachineStatus"
Text="{Binding Machine.Status.WorkStatus}" />
<TextBlock Text="生产数" />
<TextBlock Margin="10,0,50,0"
Name="_txbNumber"
Text="{Binding Machine.Status.BondNumber}" />
<TextBlock Text="信息" />
<TextBlock Margin="10,0,0,0"
Text="{Binding Machine.Message}" />
</StackPanel>
</StackPanel>
</Window>
using System.Windows;
using System.ComponentModel;
using Managed;
using System;
namespace WpfApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public Machine Machine { get; set; } = new Machine();
public MainWindow()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
private void BtnStart_Click(object sender, RoutedEventArgs e)
{
Machine.Run();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Machine)));
}
private void BtnPause_Click(object sender, RoutedEventArgs e)
{
Machine.Pause();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Machine)));
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
{
Machine.Stop();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Machine)));
}
private void BtnAdd_Click(object sender, RoutedEventArgs e)
{
Machine.Increase();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Machine)));
}
}
}