前言: 由于以前用
vb和
vc++封装过
dll和
ocx文件. 或者在网上下的类;若重新编写和封装会很费时和费力. 如
vb简单易用,对数据库操作通用类进行封装.现在
vc中可以引用之。另外由于
vc执行效率高,某些 较复杂算法或低层处理可在
vc中进行封装. 这样经常要求混用.下面是实际中个例子记,分
vb6.0,
vc++各自创建和调用四个部分 1:
vb能封装
ocx和active
dll文件. 注意 active
dll是一种动态,在被调用环境中不仅引用这个
dll,而且还要引用这个
dll所引用的其它文件,如 DBCommom.
dll 是个数据库操作类封装,它引用了Ado 接口).其创建略,具体引用见后. 2.
vc中做
dll时注意要提供接口,供其它程式调用. a>创建 win32 Dynamic-Link Library 如项目CMyTest
dll b>加入成员function(在 .h中申明 .cpp中实现) //取电脑名称 int CMyTest
dll::GetComputerNameE(LPTSTR computer_name) { DWORD buf=255; LPTSTR strname=new char[buf]; if(GetComputerName(strname,&buf)!=0) { strcpy(computer_name,strname); return 1; } return 0; } //计算两个值之和 int CMyTest
dll::Sum(int a, int b) { return(a+b); } c.>一定要加上CMyTest
dll.def 文件 ,并在在其中定义导出各个function及参数,否则不能被调用 EXPORTS GetComputerNameE @1; Sum @2; @3; d.>编译成
dll便可以.最好每个function定义int为返回值. 3.
vb引用
vc中
dll 和普通API函数一样.(
vb的long对应
vc的int) '引用 Private Declare Function GetComputerNameE Lib "目录\MyTest
dll.
dll" (ByVal strName As String) As Long Private Declare Function Sum Lib "目录\\MyTest
dll.
dll" (ByVal g As Long, ByVal b As Long) As Long '调用 Dim str As String * 20 ' 注意对于传址,一定要定义其空间大小(数组可以不指定) dim a As Long, b As Long a=25 b=78 result = GetComputerNameE(str) result=Sum(a, b) 4.
vc引用
vb中
ocx和
dll 如 C:\Test
vb\DBCommon.
dll 是个
vb做的数据库通用类。包括对表的操作。在
vc中要同时引用 DBCommon.
dll和msado15.
dll a>stdafx.h 中加上 #import "C:\Program Files\Common Files\System\Ado\msado15.
dll" no_namespace rename("EOF","adoEOF") #import "E:\backup\Recycled\Study\
vb\
vb_Report\DBCommon.
dll" named_guids(不知道类名用默认,否则用no_namespace 类名) b>在调用时先要在App中InitInstance //初始化com类 ::CoInitialize(NULL); //用指定命名来定义类型接口指针 :BCommon::_clsDBCommonPtr pdb; HRESULT hr=pdb.CreateInstance(DBCommon::CLSID_clsDBCommon); if(FAILED(hr)) { AfxMessageBox("Active
dll error",MB_ICONINFORMATION); return false; } pdb->PutDBPath(CurrentMoudlePath); //向类中传入数据库存路径 pdb->PutDBPwd((_bstr_t)strpwd); //向类中传入数据库存密码 BSTR bstrNo=_com_util::ConvertStringToBSTR(Mydata->Name.GetBuffer(0)); // BSTR bstrNo=::SysAllocString(L"Q50001"); hr=pdb->Qutation_Print_Transe(&bstrNo);//向类中传入报价单号 pdb.Detach(); //free memory==================================================================关于约定:动态库输出函数的约定有两种:调用约定和名字修饰约定。1)调用约定(Calling convention):决定函数参数传送时入栈和出栈的顺序,由调用者还是被调用者把参数弹出栈,以及编译器用来识别函数名字的修饰约定。函数调用约定有多种,这里简单说一下: 1、__stdcall调用约定相当于16位动态库中经常使用的PASCAL调用约定。在32位的
vc++5.0中PASCAL调用约定不再被支持(实际上它已被定义为__stdcall。除了__pascal外,__fortran和__syscall也不被支持),取而代之的是__stdcall调用约定。两者实质上是一致的,即函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明)。 _stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。
vc将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。 2、C调用约定(即用__cdecl关键字说明)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。 _cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。
vc将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定。 3、__fastcall调用约定是“人”如其名,它的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。 _fastcall方式的函数采用寄存器传递参数,
vc将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。 4、thiscall仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。 5、naked call采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。 关键字 __stdcall、__cdecl和__fastcall可以直接加在要输出的函数前,也可以在编译环境的Setting...\C/C++ \Code Generation项选择。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。它们对应的命令行参数分别为/Gz、/Gd和/Gr。缺省状态为/Gd,即__cdecl。 要完全模仿PASCAL调用约定首先必须使用__stdcall调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是WINAPI宏,Windows.h支持该宏,它可以将出函数翻译成适当的调用约定,在WIN32中,它被定义为__stdcall。使用WINAPI宏可以创建自己的APIs。2)名字修饰约定1、修饰名(Decoration name)“C”或者“C++”函数在内部(编译和链接)通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的,如在模块定义文件里头指定输出“C++”重载函数、构造函数、析构函数,又如在汇编代码里调用“C””或“C++”函数等。修饰名由函数名、类名、调用约定、返回类型、参数等共同决定。2、名字修饰约定随调用约定和编译种类(C或C++)的不同而变化。函数名修饰约定随编译种类和调用约定的不同而不同,下面分别说明。 a、C编译时函数名修饰约定规则: __stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_functionname@number。 __cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname。 __fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@functionname@number。 它们均不改变输出函数名中的字符大小写,这和PASCAL调用约定不同,PASCAL约定输出的函数名无任何修饰且全部大写。 b、C++编译时函数名修饰约定规则:__stdcall调用约定: 1、以“?”标识函数名的开始,后跟函数名; 2、函数名后面以“@@YG”标识参数表的开始,后跟参数表; 3、参数表以代号表示: X--void , D--char, E--unsigned char, F--short, H--int, I--unsigned int, J--long, K--unsigned long, M--float, N--double, _N--bool, .... PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代表一次重复; 4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前; 5、参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。 其格式为“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”,例如 int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z” void Test2() -----“?Test2@@YGXXZ”__cdecl调用约定:规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YA”。__fastcall调用约定:规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YI”。
vc++对函数的省缺声明是"__cedcl",将只能被C/C++调用. ============================================================================自己的经验:1,使用_stdcall协定2,调用无返回值的函数是用Sub,不要用Funcition3, 如果有参数的话,最好声明时使用Byval传递