欢迎光临散文网 会员登陆 & 注册

MFC dll实现C++/CLI dll组件,C#调用的全过程详解(附完整源码)

2023-07-09 12:28 作者:浮云绘图  | 我要投稿

模块化组件化实现独立的功能模块是软件设计的良好习惯,一般用实现为DLL。普通的DLL对外提供接口是采用导出函数接口,如果接口数量不大,只是50个以内,这种方式很适合;如果对外接口有上百个,导出函数接口就完全破坏了软件模块化分层设计的理念,使用接口非常麻烦,此情形采用C++/CLI导出类方式实现比较适合,即核心实现先C++ DLL,然后C++/CLI直接调用C++ DLL导出类,对外第三方工程提供CLI类接口。浮云E绘图以一个最简单的绘图模块为示例,详细介绍此方法的实现过程。


一、C++ DLL实现

本文只是为了介绍调用C++ dll导出类实现C++/CLI dll的完整过程,示例程序尽量简单。先用C++实现一个绘图组件dll。

C++ dll绘图主键设计构思

1. 绘图画布CFyView:CFyView继承自CWnd,是绘图画布窗口,并响应鼠标事件。

2. 绘图数据容器CChart:管理所有业务数据,(如需支持控件内滚轴,容器是虚拟画布)。

3. 曲线CCurve:曲线数据和曲线绘制。


C++ dll程序开发过程

1. 新建工程

选择C++ Windows 的 MFC动态链接库  --> 项目取名FyMfcDll,解决方案取名FyDemo --> Dll类型选 使用共享MFC DLL的常规 DLL

2. 创建绘图窗口类CFyView

新建类CFyView,继承于CWnd --> 添加窗口属性变量int m_crBackColor,重载窗口消息OnPaint、OnLButtonDblClk。

class CFyView : public CWnd

{

public:

CFyView();

virtual ~CFyView();

bool Create(HWND hParentWnd);


DECLARE_MESSAGE_MAP()

afx_msg void OnPaint();

afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);


public:

int m_crBackColor;

HWND m_hParentWnd;


CChart m_chart;

};

先创建好绘图窗口,方便调试绘图功能。接着往下实现绘图相关功能,等绘图功能实现后,再在CFyView里声明绘图相关对象,在OnPaint完成绘图呈现。

3. 创建数据管理器类CChart

数据管理类主要定义了曲线Curve对象集合、曲线标题、标题显示位置属性,以及添加、删除曲线,设置标题位置、绘图Draw等函数。

//作者:浮云E绘图,专业付费定制各类CAD/Viso等绘图编辑器、曲线控件等软件

//QQ:316868127


class CChart

{

public:

CChart();

virtual ~CChart();


void AddCurve(CCurve* curve);

void RemoveCurve(CCurve* curve);

void ClearCurves();


void Draw(CDC* dc);


void SetTitlePos(int x, int y);

void GetTitlePos(int& x, int& y);

CString GetTitle();


public:

CString m_sTitle;

CPtrArray m_curves;


int m_iTitleX;

int m_iTitleY;

};

实际商业项目中,数据管理容器管理着大量的业务对象,就曲线控件而言,比如曲线网格、坐标轴、图例等等数据。

4. 创建曲线类CCurve

曲线类主要定义了数据点集合CPoint数组、曲线名称、曲线线条宽度、颜色、线型等属性,主要方法是添加、清空点,以及画点连线函数Draw。

class CCurve

{

public:

CCurve(CString name);

virtual ~CCurve();


virtual bool AddPoint(CPoint* point);

virtual void ClearPoints();


virtual void Draw(CDC* dc);


public:

CString m_sName;


int MAX_POINT_COUNT = 100;

CPoint* m_pts = new CPoint[100]; //实际项目,此处创建的数组个数由外部程序传入

int m_nPointCount;


int m_nLineStyle;

int m_nLineWidth;

int m_crLineColor;

};

以上完成了C++ dll示例定义,完整的解决方案(包含4个工程)源码,在文本底部提供下载链接。在些C++ dll时,为了方便快捷的测试,可以先写一个C#测试工程,直接通过导出函数方式,测试C++ dll的核心流程。具体实现方式可参考 C#和VC++调用dll步骤,接口指针、字符串等类型对应关系。


C++ dll直接导出函数测试接口

extern "C" __declspec(dllexport) CFyView * NewFyChart()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

return new CFyView();

}


extern "C" __declspec(dllexport) void DeleteFyChart(CFyView* fyView)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

delete fyView;

}


extern "C" __declspec(dllexport) bool CreateFyView(CFyView *fyView, HWND hParentWnd)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

return fyView->Create(hParentWnd);

}


extern "C" __declspec(dllexport) void LoadFyVDate(CFyView * fyView)

{

CChart* chart = &(fyView->m_chart);


int offx = 50;

CCurve* curve1 = new CCurve(_T("curve 001"));

for (int i = 0; i < 20; i++)

{

curve1->AddPoint(new CPoint(offx, rand()%50));


offx += 10;

}

curve1->m_crLineColor = 0x00FF00;

curve1->m_nLineWidth = 2;

chart->AddCurve(curve1);


CCurve* curve2 = new CCurve(_T("curve 001"));

for (int i = 0; i < 40; i++)

{

curve2->AddPoint(new CPoint(offx, 100+rand() % 50));


offx += 10;

}

curve2->m_crLineColor = 0x0;

curve2->m_nLineWidth = 1;

chart->AddCurve(curve2);


fyView->RedrawWindow();

}


extern "C" __declspec(dllexport) void SetFyVBackColor(CFyView * fyView, int color)

{

fyView->m_crBackColor = color;


fyView->RedrawWindow();

}


extern "C" __declspec(dllexport) int GetFyVBackColor(CFyView * fyView)

{

return fyView->m_crBackColor;

}


C#工程直接调用C++ dll测试实例

        private const string LTDLL_NAME = "FyMfcDll.dll";

[DllImport(LTDLL_NAME, EntryPoint = "NewFyChart", CallingConvention = CallingConvention.Cdecl)]

public static extern IntPtr NewFyChart();

[DllImport(LTDLL_NAME, EntryPoint = "DeleteFyChart", CallingConvention = CallingConvention.Cdecl)]

public static extern void DeleteFyChart(IntPtr chart);


[DllImport(LTDLL_NAME, EntryPoint = "CreateFyView", CallingConvention = CallingConvention.Cdecl)]

public static extern bool CreateFyView(IntPtr fyView, IntPtr hParentWnd);

[DllImport(LTDLL_NAME, EntryPoint = "LoadFyVDate", CallingConvention = CallingConvention.Cdecl)]

public static extern void LoadFyVDate(IntPtr chart);


[DllImport(LTDLL_NAME, EntryPoint = "SetFyVBackColor", CallingConvention = CallingConvention.Cdecl)]

public static extern void SetFyVBackColor(IntPtr chart, int color);

[DllImport(LTDLL_NAME, EntryPoint = "GetFyVBackColor", CallingConvention = CallingConvention.Cdecl)]

public static extern int GetFyVBackColor(IntPtr chart);



IntPtr m_fyChart;


public Form1()

        {

            InitializeComponent();

        }


private void Form1_Load(object sender, EventArgs e)

{

m_fyChart = NewFyChart();


CreateFyView(m_fyChart, this.panel1.Handle);


LoadFyVDate(m_fyChart);

}


private void btnChangeBk_Click(object sender, EventArgs e)

        {

int bkclr = GetFyVBackColor(m_fyChart);

SetFyVBackColor(m_fyChart, 0xADD8E6);

        }


二、C++/CLI dll、 C++ dll、C#之数据类型的对应关系

 3种开发语言常用的数据类型对应关系如下表所示:

需要特别说明的是:

1>需要函数形参返回int值时,C++定义为int&,CLI定义为int%,C#定义为ref int。如GetChartSize(int& w, int& h) --> GetChartSize(int% w, int% h) --> GetChartSize(ref int w, ref int h);

2>需要颜色定义时,C++不支持Color只能用int,CLI支持Color,C#支持Color,因为int和Color类型的内存不一样,需要中间层CLI把int 与 Color相互转换(DateTime类型也是如此)。

3>CLI类加^,等同于C#的类,都是托管对象。

那么C++对象是怎么与C#对应上的呢?他们无法直接对应,得通过CLI层转换。得在CLI层同时维护C++类指针与CLI类引用,并且让他们关联上。对C#使用CCurve^托管对象,对内维护C++类CCurve*指向的非托管内存。具体请看下一节CLI工程源码。


4> 属性的实现。C++使用setXXX和GetXXX函数,CLI使用property即set和get方法,C#直接访问变量。

三、C++/CLI DLL实现

C++/CLI dll程序开发过程

1. 新建C++/CLI工程

选择 CLR类库(.NET Framework) --> 项目取名 FyClr

2. 添加FView类,作为C++窗口类CFyView的对应

// FView.h文件

using namespace System;

namespace fy_dll

{

public ref class FView : public IDisposable

{

public:

FView();

virtual ~FView();

};

}



// FView.cpp文件

#include "pch.h"

#include "FView.h"

namespace fy_dll

{

FView::FView()

{}


FView::~FView()

{}

}

1> FView是托管类,带ref关键字;

2>FView类继承于IDisposable,作为C++资源对象(窗口)的对应类,请继承于IDisposable

3>请定义一个namespace 命名空间,在C#工程好引用。


C++对象与C#对应

    //FView.h

    #include "../FyMfcDll/CFyView.h"


    public ref class FView : public IDisposable

    {


public:

FView();

property Color BackColor {

Color get();

void set(Color value);

}


private:

CFyView* m_cFyView;


    ......

    };



    //-----------------------------------------------

    //FView.cpp

    FView::FView()

{

m_cFyView = new CFyView();

}


Color FView::BackColor::get()

{

return UInt2Color(m_cFyView->m_crBackColor);

}

void FView::BackColor::set(Color value)

{

m_cFyView->m_crBackColor = Color2UInt(value);

}

是在ref class FView类里定义了一个C++对象指针,维系这与C++ dll的关系。


C++/CLI dll封装C++ dll过程和编译选项

1> 回到C++工程,把需要导出的类定义加关键词

class  CFyView : public CWnd  --> 改成 class __declspec(dllexport)  CFyView : public CWnd

2>CLI工程编译选项设置

        a> 项目属性 ->  配置属性 -> 高级 ->  MFC的使用,设为"在共享DLL中使用MFC";

        b> 项目属性 ->  配置属性 -> 高级 ->  字符集,设为"使用多字节字符集;(如果文字乱码)

        b> 项目属性 ->  配置属性 -> 调试 -> 命令,可设为调用此dll的应用程序EXE;(非常重要,方便调试代码)

        c> 项目属性 ->  配置属性 -> 调试 -> 调试器类型,设为"混合(.NET Framework)";(方便调试代码)

        d> 项目属性 ->  链接器 -> 常规 -> 附加库目录,设为"$(SolutionDir)$(Configuration)\";

        e> 项目属性 ->  链接器 -> 输入 -> 附加依赖项,设为"$FyMfcDll.lib";(因为本项目CLI工程调用了C++ FyMfcdll工程dll)

         f> 项目属性 ->  C/C++ -> 代码生成 -> 结构成员对齐,设为"4字节";(如果C++工程与CLI工程对通一结构体内存数据错了,两个工程需设置相同的结构成员对齐方式)

         g> 项目属性 ->  配置属性 -> 高级 -> 公共语言运行时支持,设为"公用语言运行时支持(/clr)";(C++工程与CLI工程都要设置)


3. CLI工程,继续新建托管类FChart对应C++工程的CChart类,新建托管类FCurve对应C++工程导出类CCurve。

C++ 工程类如CChart定义改成class __declspec(dllexport)  CChart  --> CLI工程FChart类新增有必要对外提供接口访问的属性和成员函数(实现是调用C++指针对象执行)。工程源码底部下载。


四、第三方调用C++/CLI DLL示例

1. 新建项目,选 C# Windows 桌面  --> Windows 窗体应用(.NET Framework) --> 取名“TestClrDllDemo”

2. 引用上文开发的Clr组件。1>添加代码 using yf_dll  --> 2> 点击“引用”,右键“添加引用”,浏览clr组件生成目录,选择fyClr.dll。

3. C#工程直接调用CLI DLL里的各种类。


 解决方案可以运行,包含4个工程:(完整源码下载地址)https://download.csdn.net/download/fyhhack/87991560

1. MFC dll工程 FyMfcDll,C++ MFC实现核心业务和绘图。

2. C++/CLI dll工程 fyClr ,封装 MFC dll各功能,以导出类方式对外提供接口,直接C#访问。

3. C# Winform测试MFC dll工程 TestMfcdllDemo

4. C# Winform测试C++/CLI dll工程 TestClrDllDemo


MFC dll实现C++/CLI dll组件,C#调用的全过程详解(附完整源码)的评论 (共 条)

分享到微博请遵守国家法律