深入浅出CChart 每日一课——快乐高四第三十八课 二次创业,GNU环境之MingW控制台绘图

这节课我们开始另一个话题,这个话题要更加深入一些。

首先给出网络上的一段话。

通常所说的GCC是GUN Compiler Collection的简称,除了编译程序之外,它还含其他相关工具,所以它能把易于人类使用的高级语言编写的源代码构建成计算机能够直接执行的二进制代码。GCC是Linux平台下最常用的编译程序,它是Linux平台编译器的事实标准。同时,在Linux平台下的嵌入式开发领域,GCC也是用得最普遍的一种编译器。GCC之所以被广泛采用,是因为它能支持各种不同的目标体系结构。例如,它既支持基于宿主的开发(简单讲就是要为某平台编译程序,就在该平台上编译),也支持交叉编译(即在A平台上编译的程序是供平台B使用的)。目前,GCC支持的体系结构有四十余种,常见的有X86系列、Arm、PowerPC等。同时,GCC还能运行在不同的操作系统上,如Linux、Solaris、Windows等。

这节课我们就来看看CChart怎么在GNU环境中使用。在Windows系统中,GNU编程环境貌似MingW比较好,我们就来在MingW中测试一下CChart。

在MingW中想调用VC编译的CChart Dll文件,首先要考虑的是gcc编译器(或者说g++)怎么识别VC Dll文件里面的函数。

C++里面有一个概念是Name mangling,翻译成名称修饰或名称粉碎,也就是一个函数名在Dll里面的实际名称,前面已经多次遇到了这个问题。例如,CChart里面的一个简单函数,void CreateChart(),在名称粉碎后,变成了?CreateChart@Classless@@YAXXZ这么一个复杂的形式。名称粉碎的目的之一,是为了实现函数的重载,也就是同名函数。也有办法让C++不进行名称粉碎,方法也很简单,就是在定义函数时,在前面加上extern “C”就可以了,也就是采用C语言的命名方式。不幸的是,CChart里面有大量重载的同名函数,这个简单的方法不能用。

怎么解决这个问题呢?笨笨想的办法是对CChart库的Dll文件进行二次包装,新建一个Dll文件,包装部分需要用到的CChart函数。当然了,这个时候,同名函数一般只能包装一个。如果要包装多个同名函数,也是可以的,只是包装后的函数名需要修改,以避免冲突。

A38.1 CChart库的二次包装

二次包装需要新建DLL项目,这个过程虽不复杂,但也有一些工作量。为此,笨笨已经建立好了一个模板,名为CChartWrapper,大家只需要往里填很少的内容就可以了。

其实笨笨建立的这个模板也很简单,简单的原因是用了一项名为Dll转发的技术。据说这项技术是X客专用的哟!不过笨笨可不懂他们的技术,笨笨是规规矩矩的正派人,哈哈。

Dll转发的方法就归结到VC一项预处理指令,下面给出一条完整的指令说明一下。

#pragma comment(linker, "/export:_CreateChart=CChartu.?CreateChart@Classless@@YAXXZ")

就这么一行语句,就实现了void CreateChart();这个函数的转发。

注意到几点。

首先,我们是对CChartu.dll文件里面的函数进行转发的,一般情况下,我们还是尽可能使用宽字节的版本吧。

其次,?CreateChart@Classless@@YAXXZ就是CreateChart这个函数在CChartu.dll里面的实际名称,和前面的课程一样,这个名称需要到CChartu_EXPORTS.txt里面去查询。

第三,导出名称是_CreateChart,我们在名称前面添加了一个下划线。当然也可以改成任意的名称,只是前面的下划线是必须的。

为什么要加这条下划线呢?这就是VC在C语言环境中对cdecl调用约定下的函数命名规则。幸运的是,gcc的命名规则和VC是一致的,这让我们减少了很多麻烦。

怎么引用这个函数呢?很简单,按下面格式声明一下就可以了。

extern “C” void CreateChart();

这里的函数名前面可不能再加下划线了。

下面打开CChartWrapper工程中的ChartWrapper.cpp,在里面按照上面的格式转发几个我们将要用到的函数。

#pragma comment(linker, "/export:_CreatePopupChartWndGlobalWithMsgLoop=CChartu.?CreatePopupChartWndGlobalWithMsgLoop@Classless@@YAPAUHWND__@@PAU2@HPA_WHHHH@Z")
#pragma comment(linker, "/export:_AddPoint2D=CChartu.?AddPoint2D@Classless@@YAHNNHH@Z")
#pragma comment(linker, "/export:_SetTitle=CChartu.?SetTitle@Classless@@YAXPB_W@Z")
#pragma comment(linker, "/export:_ReDraw=CChartu.?ReDraw@Classless@@YAXXZ")

这里仅仅转发了四个函数。

编译,生成我们需要的CChartWrapperU.dll。这样,我们的二次包装就完成了。

 

A38.2 在MingW中调用CChart

下面演示一下在MingW环境中使用上面二次包装函数的方法。

这里,我们采用的是MingW32,不是64位版本。

第一步,建立一个C++源文件,名字为GccConsole.cpp,内容如下:

#include <stdio.h>

extern "C" int	CreatePopupChartWndGlobalWithMsgLoop(int hWndParent, int nChartType, wchar_t *wndTitle=(wchar_t *)L"Title", int x=0, int y=0, int cx=(int)0x80000000, int cy=(int)0x80000000);
extern "C" int	AddPoint2D(double x, double y, int nDataIndex=0, int nPlotIndex=0);
extern "C" void SetTitle(wchar_t *title);
extern "C" void	ReDraw();

int main()
{
	CreatePopupChartWndGlobalWithMsgLoop(0, 0);
	for(int i=-10; i<=10; i++)
	{
		AddPoint2D(i, i*i);
	}
	SetTitle((wchar_t *)L"Gcc程序标题");
	ReDraw();
	getchar();
}

很简单吧!

首先,我们把上一小节里面转发的Dll函数在这里重新声明了一下,以便调用。

其次,为了避免麻烦,我们把HWND类型的参数声明改成了int类型,这样就不需要包含windows的专用头文件了。

第三,这里我们用的是CreatePopupChartWndGlobalWithMsgLoop这个函数来建立一个弹出窗口,这和前面快乐高四第三十四课里面CreatePopupChartWndGlobal函数是差不多的,只是不需要再调用SimpleMsgLoop这个函数了,函数里面自带了消息循环。

第四,代码最后加了一句getchar,我想这是最基本的技巧了,初学C语言的人都会,用于暂停程序的执行。这里这句话是必须的,不然程序直接退出了。当然暂停程序执行还有别的办法,比如Sleep。

第二步,把上一小节编译获得的CChartWrapperU.dll拷贝到GccConsole.cpp文件目录里面,同时把CChartu.dll也拷贝过来。

这里两个Dll文件都是需要的,CChartWrapperU.dll文件只起到了一个函数转发的作用,并没有实际的内容。

第三步,调用gcc命令进行编译。

gcc –o GccConsole.exe GccConsole.cpp CChartWrapperU.dll –finput-charset=GBK

程序简单,这里并没有使用makefile。

由于程序字符串里使用了中文,所有这条命令最后添加了一个选项-finput-charset=GBK,不然编译出错。如果没有中文字符串的话,这个选项可以不加。

结果如图。

好了,下课!

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值