您的当前位置:首页利用动态链接库实现C++与VB的混合编程

利用动态链接库实现C++与VB的混合编程

2022-10-23 来源:乌哈旅游
2010年第12期 中图分类号:TP311.11 文献标识码:A 文章编号:1009—2552(2010)12-0135—05 利用动态链接库实现C++与VB的混合编程 朱荟桥 (西南交通大学,成都610031) 摘 要:C++与Visual Basic是目前使用最广的两种通用语言,两种语言各有优点,首先阐述了 混合编程的目的和优势,探讨了为什么要混合编程,然后介绍了利用动态链接库实现这两种语 言的具体方法,最后举了三个混合编程的例子,这些例子应用了文中介绍的混合编程的方法, 也展示了混合编程的优势。 关键词:混合编程;C++;Visual Basic;动态链接库;剪贴板 Blend programming with C++and visual basic by using dynamic link library ZHU Hui—qiao (Southwest Jiaotong University,Chengdu 610031,China) Abstract:C++and Visual Basic are the most popular programming language at present,each one has different merit.Firstly,this article expounds the purpose and advantage of blend programming,describes the necessity of blend programming,then introduces concrete method to implement blend programming by using dynamic link library.At last,three examples which apply the methods introduced by this article was cited,also,these examples illustrate the advantage of blend programming. Key words:blend programming;C++;Visual Basic;dynamic link library;clipboaM 1 混合编程的目的与优点 利用各种语言自身优点等目的上,有着很广的应用。 我们知道,程序设计语言和工具种类很多,混合 C++与Visual Basic是目前使用最广的两种通用语 编程是指在使用两种及以上的程序设计语言来编写 言,c++是实现面向过程、面向对象的一种极好的语 一个程序,c++语言具有编写数值计算算法简便,程 言,C++适于编写基于函数的结构化程序,是描述算 序结构简单,调试方便的优点,C++的STL(Standard 法实现算法的程序化的一种极好语言。Visual Basic Template Library,标准模板库)提供了大量的容器和 是基于对象由事件驱动的一种语言,用其编界面很 算法,这些容器封装了大量的数据结构,并实现了泛 容易,而且Visual Basic内置控件多,可引用的类多, 型,而STL中的算法实现了诸如排序、向量内积等 使得Visual Basic在访问注册表、访问数据库等操作 基础算法,这就更加方便了数值计算的程序设计。 上很方便,相比于其他语言来说,用Visual Basic编 而Visual Basic具有编写界面容易的优点。本文试 界面的时间和代码耗费都是很小的。 结合c++与Visual Basic的优点,探讨这两种编程语 各种工具都有其适宜解决的问题,认识到两种 言混合编程的目的与优点、混合编程的方法。最后 语言不同的优点后,容易想到,将两种语言结合起 给出了一些实际的例子来应用这些理论方法并展示 来,进行优势互补,以最小的代码量、最简单的方法、 这两种语言混合编程的实际优势。 最短的时间来编写出所需要的程序,也就是用最高 程序设计语言种类很多,而不同的语言有各自 收稿日期:2010—07—10 的优点。混合编程在开发大型软件、多人合作、充分 作者简介:朱荟桥(1987一),男,在读硕士研究生,主要从事隧道工 程计算机数值模拟的研究。 一135— 的效率解决实际的问题,这就是两种语言混合编程 的目的,因为对于具体的问题,可能用不同的语言来 编程,效率的差异非常地大,这就要求在具体问题上 选择合适的方法,以最高的效率来解决具体的问题。 总之,混合编程的优点就在于能够充分利用各种语 表1 C++与Visual Basic的类型对应关系 言的优点,更高效率的开发出程序。 2 混合编程的方法 C++与Visual Basic混合编程的方法有很多种, 比如直接法、动态链接库法、ActiveX控件法 等, 本文介绍最简单的一种方法:使用DLL(Dynamic Link Library,动态链接库)。这种方法是各种方法 中技术难度最低,对程序员的要求最低而且编写方 法最简单的一种,鉴于简单实用的目的,我们探讨这 种方法,使用c++编写DLL,在Visual Basic中进行 调用。主要的问题转化为数据传递与程序通信的问 题。本文介绍三种方法来实现使用C++编写DLL, Visual Basic调用的方法:第一种是直接通过函数参 数来传递数据;第二种是通过文本文件来传递数据; 第三种是使用Clipboard(剪贴板)来传递数据。很 多文献¨ 都只侧重于第一种方法,但这种方法不 一定适用于所有的问题,对于具体问题也不一定是 最有效率的。在介绍这三种方法以前,首先简要介 绍一下用Visual C++6.0编写DLL需要注意的一 些问题:在新建工程的选项卡里选择Win32 Dyna mic-Link Library,代码的编写与普通的C++控制台 (Console)程序没有区别,但在工程中必须加入def 文件,在def文件中声明哪些函数需要作为外部接 口即哪些函数可被其他程序访问,否则在用Visual Basic进行调用时会出现找不到函数入口的问题。 具体做法是在def中加入EXPORTS段,如下所示 (例如需要导出的函数名为sample): EXPORTS sample@1 另外,还要说明一个问题,用Visual C++6.0编 写的DLL可分为三种类型:Non—MFC DLL(Win32 DLL)、Regular DLL(常规DLL)、Extension DLL(扩 展DLL),Win32 DLL不用MFC的类库结构,导出的 函数是标准的c接口_2 J,能被所有的应用程序识别 和调用,本文讨论和编写的也是这种类型的DLL。 2.1 直接通过函数的参数来传递 首先要搞清c++与Visual Basic的类型对应关 系,如表1所示。 一136一 尤其要注意的是,C++的int型(4字节)对应 Visual Basic的Long型(4字节),也就是说c++的 int和long两种类型是没有区别的。Visual Basic的 Boolean型是2个字节,C++的bool型为1个字节, 两者没有对应关系,Visual Basic的字符串类型 String和c++的string类也没有对应关系,若需要传 递逻辑型数据建议直接采用长整型long,两种语言 均为0代表flase,非0代表true。字符串建议采用 剪贴板的方式传递,结构体数组建议采用文本文件 的方式来传递。另外,C++的句柄类型对应Visual Basic的Long型。 如果传递的是单个变量,那么直接按表1的对 应关系进行值传递(Visual Basic的参数前加Byval, c++的参数为非指针类型)即可,若是传递数组,则 方法比较特别:要传递数组的长度,又要传递数组的 第一个元素。而且前者是值传递,而后者是地址传 递(Visual Basic的参数前加ByRef或什么也不加, C++的参数为指针类型或数组类型)。比如函数名 叫sample,需要传递一个长度为20的整型数组,在 C++中的定义应该是: LONG sample(1ong 1"1,long buffer) 假设DLL文件名为1.dⅡ,那么在Visual Basic 中的定义应该为: Declare Function sample Lib”1.dll”(ByVal n As Long,ByRef buffer As Long)As Long 在Visual Basic最终调用函数的语法是(a为数 组名): sample 20,a(0) 而不是: sample 20,a() 注意,这是Visual Basic的语法的关键。还需要 注意的是第一个参数是数组的元素的总个数,而不 是数组占用的内存大小。 2.2通过文本文件来传递数据 这种方法是传递自定义结构体(Visual Basic为 Type,C++为struct)、大量数据的一种极好方式,这种 方法的要点是先将待处理的原始数据写人文本文件, { 再调用DLL中的函数进行处理,将处理好的目标数 private: 据读人Visual Basic程序中,最后再删除用于传递数 HWND hwnd; 据的中间文件。可以使用API函数DeleteFile来进行 public: 删除操作。这种方法是3种方法中操作最简单的一 SCO—Clipboard(); 种。在编写代码上也是技术难度最低的一种。 bool clear(); 2.3通过Clipboard(剪贴板)来传递数据 bool SetText(char jlc); Clipboard就是一块内存区域,我们平时经常用 boo1 SetText(string); 到的复制粘贴操作就是通过Clipboard进行的。使 void GetText(string&); 用Clipboard来传递数据是一种快捷方便的方法,使 }; 用Clipboard不仅能读写文本格式数据,还可以读写 该类中的函数成员GetText和SetText即是用来 二进制格式数据,为了简便,本文只讨论使用文本格 读写Clipboard。利用上述8个API函数即可ADT 式来传递数据。可以在程序的运行过程中先将待处 的详细实现。限于篇幅,这里只列出最关键的Set. 理的原始数据拷贝到Clipboard中,然后调用DLL中 Text和GetText两个方法的详细C++实现代码: 的函数从Clipboard中读出数据,处理完毕之后再通 void SCO—Clipboard::GetText(string&S) 知Visual Basic主程序从Clipboard中读出已处理好 { 的目标数据即可。下面介绍Visual Basic和C++访 HGLOBAL h; 问Clipboard的方法。Visual Basic访问Clipboard非 char buffer=NULL; 常简单,因为Visual Basic有个内置对象叫Clip— OpenClipboard(hwnd); board,就是用来操作Clipboard的,直接使用该对象 h=GetClipboardData(CF_TEXT); 的GetText方法和SetText方法即可完成对Clipboard buffer=(char )GlobalLock(h); 的读写操作。对于c++来说就没有内置的对象可 S=buffer; 以直接操作Clipboard,本文给出一种c++操作Clip. GlobalUnlock(buffer); board的方法,通过使用API函数编写一个类,用该 CloseClipboard(); 类来实现Clipboard的操作。该类所用到的API函 } 数有: bool SCO—Clipboard::SetText(char}C) ( ̄)GlobalAlloc:动态分配一块全局的内存块,返 { 回一个句柄。 HGLOBAL h,hl; ( ̄)GlobalLock:锁定用GlobalAlloc分配出的内 if(OpenClipboard(hwnd)) 存块,并返回其指向首地址的指针。 { ③GlobaIUnlock:与Globall_ ̄ck配套,用于释放 h=GlobalAlloc(GMEM—FIXED,strlen(C) 内存。 +1); ( ̄)OpenClipboard:该函数的参数是一个窗口句 hl:GlobalLock(h); 柄,用于打开剪贴板,剪贴板与窗口是关联的。 memcpy(hl,C,strlen(C)+1);  ̄EmptyClipboard:清空剪贴板,在使用这个函 SetClipboardData(CF_TEXT,h1); 数前必须成功执行OpenClipboard函数,否则函数执 GlobalUnlock(hi); 行将失败。 CloseClipboard(); ⑥setclipboardData:向剪贴板写人数据。 return true; ( ̄)GetClipboardData:从剪贴板获取数据。 } ( ̄)CloseClipboard:与OpenClipboard配套,用于 else return false; 关闭用OpenClipboard打开的剪贴板。 } 将这个类取名为SCO—Clipboard,这个类的C++ bool SCO—Clipboard::SetText(stirng S) ADT(Abstract Data Type,抽象数据类型)如下所示: { class SCOClipboard HGLOBAL h,hl; ——一1 37— if(OpenClipboard(hwnd)) 1 h=GlobalAlloc(GMEM—FIXED,S.1ength() +1); hl=GlobalLock(h); memcpy(hi,S.c_str(),S.1ength()+1); SetClipboardData(CF_TEXT,h1); GlobalUnlock(h1); CloseClipboard(); return true; } else return false; } 这样,c++和Visual Basic都可以访问Clipboard 了,通过对这块内存的读写,就能达到数据传递的目 的。 以上就是本文要介绍的三种混合编程的方法, 当然,正如编程语言没有好坏之分,能有效率的解决 问题的语言就是好语言,具体的方法也是一样,对于 具体的问题,只有最适于解决的方法,没有最好的方 法。参考文献[1—3]只详细讨论了第一种方法,但 在下面的例子中能看到,以上介绍的3种方法都大 有用武之地,配合使用更容易解决实际的问题。 3 混合编程的应用实例 3.1 Joseph问题 Joseph问题的一般提法是这样的:有n个人围 成一圈,从第一个人到最后一个人分别编号为l到 n,从第r个人开始报数,报到m的人出圈,并从下一 个人开始重新从1开始报数,报到rn的人出圈,一 直反复下去直到只剩最后一个人,求出圈顺序及最 后剩下的那个人的序号。 这是一个很经典的模拟问题,很多程序设计的 教材以及一些数据结构和算法的教材都将其选择为 习题,这个问题可以不需要特定的算法,只需按照要 求每次删除一个即可,下面介绍C++STL容器vec— tor的erase方法的一个优点:不仅能自动将数组的 长度减l,而且能够自动的维护数组下标,即删除操 作执行以后,剩下的数据的数组下标自动变成0到 数组长度减1,而相对位置不会有任何变化,那么利 用c++的容器vector来解决这个问题将是编写代码 最简单的方式,我们用C++做计算内核,Visual Bas— ic只用来做窗口界面,接受用户数据的输人及显示 输出,最后剩下的人的序号通过函数的返回值来实 一】38一 现传递,而出圈顺序通过Clipboard来传递。 将C++函数原形定义为:long joseph(1ong n, long ITI,long r),在Visual Basic中的声明为(假设编 译为1.du): Private Declare Function joseph Lib”1.dll”(By— Val n As Long,ByVal m As Long,ByVat r As Long) As Long 在这个函数中,最后剩下的人的序号作为函数 的返回值,而模拟出圈的顺序写入Clipboard,Visual Basic程序只需从Clipboard中读出,将其显示在文 本框里即可。 这里介绍一下C++程序编写的关键点,首先, 怎样用一维数组结构来模拟本问题的环形链表结 构,我们可以先定义一个整型数P,用来保存当前元 素的数组下标,每次P对数组的长度取模就能实现 问题要求的环形运动;其次,是vector容器的erase 方法的使用,vector的迭代器是随机访问迭代器,也 就是说可以与整数做加减法,当要删除当前P指向 的元素,只需用代码v.erase(v.begin()+P);即可 (v为veteor容器变量);最后,关于怎样将出圈顺序 写入Clipboard的问题,我们看到定义的用于操作 Clipboard的ADT中SetText方法的参数类型是 String,只需用c++的字符串流stringstream类,在每 次删除元素是将待删除的元素的值写入字符串流, 最后利用stringstream类的str方法,即可将字符串 流转换为string类型的数据。 3.2简单的样本统计 这个实例主要是为了阐明利用c++处理数据 的问题,这个实例的样本统计即是根据已有的样本 计算样本的平均值和方差还有标准差,可以说,问题 本身非常简单,但是在现实问题中,往往样本的数据 量很大,而且样本的容量未知,若用Visual Basic来 处理的话,可以定义一个很长的数组,但这样做既有 数组长度不够的可能,也有大量浪费内存空间的可 能,这时使用C++的数据流对象来处理就非常容 易,若把样本数据间用一个空格隔开,整个样本的数 据放在一个文本框中,用c++的字符串流来将数据 解析开是非常容易的,通过Clipboard来传递样本, 用一个长度为4的数组来作为统计结果的接收空 间。比如在Visual Basic中的一个文本框控件,把数 据输入,数据间只需用空格隔开。将文本框的内容 按字符串用剪贴板传递给c++函数,即可算出要的 4个值。这4个值是按地址传递的方式传递数组, 在c++函数中算好返回的。 c++中计算的样本函数定义为: VOID sta(double a ) 在Visual Basic中的函数声明(假设编译为1. dl1): Private Declare Sub sta Lib”1.dul”(a As Double) 除,从文本文件读入已排序的数据再显示即可,最后 使用API函数DeleteFile删除生成的临时文本文件 即可。在传递数据时必须在Visual Basic和c++程 序里定义相同的结构体。Visual Basic可用Input和 Print命令来分别完成文本文件的读人和写出,C++ 可用ifstream和ofstream两个类来完成文本文件的 下面说明在c++中的具体实现方法,在Visual Basic调用c++函数前,先将文本框的内容用Visual Basic的内置对象Clipboard的SetText方法将文本 框的内容拷贝,在c++函数中先读人到字符串流当 中,再用流提取符即可将数据读人vector容器,C++ 的字符串流可以自动判定是否已到流的末尾,其余 的计算就很简单了。 这个程序充分体现了Visual Basic和c++混合 编程的优点:利用了Visual Basic做界面容易、C++ 做数值计算容易,这个程序的Visual Basic部分只有 短短10行。当然,这个例子只是示意如何传递数据 和如何将数据按要求读人程序开辟的内存空间中。 这个程序充分利用了C++STL内置的容器动态的 数据结构对数据读人灵活性强的优点。对于其它的 数值计算问题也可仿照这种方法传递数据和做 界面。 3.3 Visual Basic的ListView控件的按字段排序问题 ListView控件是Visual Basic中用于显示数据, 尤其是表格式结构数据的一种常用控件,往往我们 需要按这个表格不同的字段进行排序操作。尽管 ListView控件本身提供了这一功能,但是无论字段 是什么类型都只能按字符串类型进行排序,如果需 要按数值来排序,就有可能排序失败。 ListView控件内置的排序功能按字段1排序的 方法:在设计时将ListView控件的Sorted属性设置 为True,然后使用代码:ListView1.SortKey:1,我 们需要按数值大小进行排序,但ListView控件内置 的排序功能只能按字符串类型进行排序,这是无法 满足要求的,这个问题已是一个较为平常的问题,很 多使用ListView控件的用户都会遇到,网上也有不 少解决的方法,这里我们使用c++来进行排序,编 码难度是很低的,采用文本文件的方式进行传递,调 用C++STL算法sort可以对具有随机访问迭代器 的容器进行排序,也就是说,如果使用C++来进行 排序的话,只需调用C++的库函数,并不需要自己 写排序算法,只需将现有的ListView里的数据写入 文本文件,然后c++函数读人,排序之后再写入文 本文件,Visual Basic的ListView控件将原有数据清 读写。 由于使用sort函数的排序对象是结构体数组, 两条记录之间的比较规则必须自己定义,否则C++ 无法进行排序操作,按不同字段排序就有不同的比 较准则,一个字段就要写一个谓词(Predict)函数,即 bool型的函数,假设要按字段1排序,我们在c++定 义的结构体取名为SCO,字段l为结构体的数据成员 f1,那么该谓词函数的编写如下所示: bool pred(const sco&sl,const sco&s2) { retum(s1.fl<s2.f1); } 将这个谓词函数作为sort函数的第三个参数, 就能按该字段排序,注意上面用的是小于号,那么最 终的排序结果为升序,即数值小的在前,若改为大于 号则为降序排序。一个字段就需要编写一个谓词函 数,在Visual Basic的操作中用组合框来选择按哪个 字段来排序,这只需在C++中选择不同的谓词即 可。在C++函数的设计上可以多设计一个参数,这 个参数用来标识需要按哪个字段来排序,也就是需 要调用哪个谓词函数。 4 结束语 通过上面三个例子,我们看到了混合编程的优 势,编写代码更简单,编程的效率也更高。本文介绍 的三种传递数据的方法基本上可以满足需要。当 然,编写程序是需要不断的积累和总结,以最高的效 率解决问题才是最终目的。从上面的例子看出,混 合编程是一种强大的编程手段,在提高编程效率上 大有用处。 参考文献: [1]王保华.VC与VB混合编程的几种方法[J].电脑编程技巧与 维护,2002(9). [2]顾可民,白殿生.基于在程序设计中混合使用VC/VB编程的研 究[J].沈阳工业大学学报,2005(1). [3]叶娜,许惠平.DLL实现VB与VC混合编程的关键技术研究 [J].科技资讯,2007(io). 责任编辑:张荣香 一l39— 

因篇幅问题不能全部显示,请点此查看更多更全内容