VC和VB的混合编译
VC和VB的混合编译
原理:
Visual Basic 从5.0起就可以象C++那样将程序编译成本地码.VB将程序中的每个form, bas, cls, vbp 编译为obj文件,然后再调用连接程序将它们连接成EXE, 这个过程是自动的,连接完成后它会自动删除中介生成的OBJ文件. 要实现我们的目的就需要在连接程序连接输出EXE之前对OBJ文件进行处理,将C++编译的OBJ文件换进去.
准备:
我们需要自己些一个连接程序,当然这个程序不是要实现连接的功能,只需要实现预先处理 OBJ 文件, 获取VB传递给连接程序的参数, 对参数进行必要的修改, 然后调用原连接程序完成连接工作. 我们将vb目录下的 改名为 , 再将我们写的 放到这个目录中. 这个Fake Link 程序我已经完成了, 在压缩包包中可以到.
先讲讲这个Fake Link 程序是怎么工作的:
首先程序取得 VB 传递给连接程序的参数.
通过对参数进行分析获取当前所编译的VB工程文件的完整路
径.(如:F:\ LinkWithVC\ LinkWithVC.vbp), 然后程序会查该工程对应的自定义连接配置文件(F:\LinkWithVC\ LinkWithVC_link.ini), 如果不到程序什么也不做直接调用
如果到了,就读取里面的设置,按照设置进行相应的处理,最后调用
InI文件的配置将在下面实践测试中做介绍.
实践测试:
运行VB6新建一个工程,工程名设为LinkWithVC, 给工程添加一个模块(bas), 名称设为 ModVC.
给窗体添加一个按钮名称 cmdTest, 标题 Test. 在ModVC中添加
Public Function Test() As Long
Test = 9
End Function
在cmdTest的Click事件中添加代码 msgbox test, 保存工程,编译运行,点击按钮我们会看到
是9没错就是 9 .
现在我们启动VC6新建一个空的Win32工程名称就叫vcobj,目录就设置为VB工程LinkWithVC所在的目录.
在工程中添加一个Generic Class ,类的名称就叫ModVC, 设置活动配置为 Win32 release. 现在就点
击 Build 菜单中的 Compile ModVC.cpp 将它编译成OBJ文件.
然后在VB工程目录下建一个文件 LinkWithVC_link.ini.在文件中输入如下内容: [Settings]
lib=0 log=1
[OBJ] F:\LinkWithVC\vcobj\Release\ModVC.obj=F:\LinkWithVC\ModVC.obj
[lib] 注:F:\LinkWithVC是VB工程所在目录,F:\linkWithVC\vcobj是VC工程所在目录.
Ini文件有三个段.Settings段有lib 它表示需要附加的lib的数量. 相应的lib在lib段中定义. OBJ段就是OBJ替换定义段可以是 vc的obj=vb的obj. Fake Link程序会自动用vc的obj替换vb的obj文件.
Log为1表示生成连接记录. 有记录便于排错.
夷齐清风广场舞
好了现在我们切换到VB工程来生成
完了我们会发现并没有生成EXE, 打开log文件看看,会看到如下内容:
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
Form1.OBJ : error LNK2001: unresolved external symbol "private: void __stdcall ModVC::Test(void)" (?Test@ModVC@@AAGXXZ)
F:\ : fatal error LNK1120: 1 unresolved externals
看到拉,连接出错了 unresolved external symbol private: void __stdcall ModVC::Test(void) ,这个是正常的,我们替换了ModVC.obj,而我们的VC的modvc.obj并没有定义这个函数.接下来就是解决这个问题了.知道怎么做了吧,…现在切换到VC,在类ModVC中定义一个private的函数void __stdcall Test();函数体先空着.再编译它. 然后切换到VB生成EXE.好了生成了EXE文件了,我们运行它,点击按钮,会看到:
是10不是9,显然替换成功了,可是为什么是10呢…我也不清楚….(:P)
Void的函数怎么返回值呢? 在Windows中函数返回值一般都是存放在eax中的, 我们来试试.在 Test 的函数体中添加代码 _asm mov eax , 123, 再重复前面的操作生成EXE,运行:
在上一回我们已经实现VC,VB代码的混合编译,并成功的在VB代码中调用了VC代码中的一个无参数的函数,并取得了函数的返回值. 这一回我们将实践一下如何调用带参数的函数.
打开上一会的两个工程(LinkWithVC, vcobj). 切换到VB在form上添加一个按
钮 name: cmdTestLong, 标题 TestLong. 在form上添加一个文本框txtInput 内容填 8. 再在ModVC中添加如下代码:
Public Function TestLong(ByVal lng As Long) As Long
TestLong = -1
End Function
在cmdTestLong的Click事件中添加代码 msgbox Testlong(clng(txtInput.Text))
现在我们来生成EXE. 显然生成会失败,打开log文件看看.
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
Form1.OBJ : error LNK2001: unresolved external symbol "private: void __stdcall ModVC::TestLong(v oid)" (?TestLong@ModVC@@AAGXXZ)
F:\ : fatal error LNK1120: 1 unresolved externals高考成绩查询咋查
看到了吗,和第一次看到的错误Log除了函数名,完全是一样的……这时候想到了什么? 参数呢? 在VB 代码中明明是有参数申明的啊? ….先不管它, 和上回一样在VC类中添加一个名为TestLong的同类型的空函数. 编译vc的obj,再生成VB的EXE,运行点击 TestLong按钮, 我们看到msgbox提示8, 和我们的参数8 一样!! 是巧合? 试试改改 txtInput 中的数字. 再点击TestLong按钮. 我们会发现 msgbox提示的和txtInput文本框中的数字是一样的. 有了上回返回值的经验, 而我们的函数什么也没有做, 如是我们会知道这个参数就是存放在 eax中的(注:windows中fastcall的函数参数是采用寄存器传递的), 好了到了这里我们知道怎么在VC代码中获取 vb代码传递的参数了. 可是在传递一个 long 型的数据有什么实际用途呢?  一个 long 它就意味着一个长整形数字、一个句柄、一个指针….我们来看一个实际的例子。
在Form上添加一个按钮cmdTestHwnd在按钮 TestHwnd的 Click事件中添加代
码 call TestLong(txtInput.hWnd)
这里我们传递的是一个textbox的句柄。
我在VC函数中添加如下代码
void ModVC::TestLong()
{    long hw;
_asm mov hw,eax; //取得参数(TextBox的句柄)
SetWindowText((HWND)hw,"VC: Hello VB");
}
重复操作,生成我们的EXE,。。。我们会发现失败了,打开log看看。Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
ModVC.OBJ : error LNK2001: unresolved external symbol __imp__SetWindowTextA@8
F:\ : fatal error LNK1120: 1 unresolved externals
熟悉VC的一看就知道连接的时候没有把User32.lib连接进去。
解决方法有两个1个是在VC代码中使用LoadLibray,GetProcessAddress的方式调用这个api,另一个方法就是让连接器把user32.Lib连接进去。显然如果要大量使用api函数的话第一中方法太繁琐。我们用第二个方法。怎么做了呢?还记得上一回的自定义连接配置文件吗。[Settings]
lib=1 log=1
[OBJ] F:\LinkWithVC\vcobj\Release\ModVC.obj=F:\LinkWithVC\ModVC.obj
[lib] 1=User32.lib
Settings段的lib 表示 [lib]段中的lib数目。在lib段我按照顺序1=…,2=… 添加我们要连接的lib即可,当然也可以是obj文件。
好我们再来操作,生成EXE,OK。运行点击TestHwnd。好了我们看到TextBox中的文字变成
了 VC: Hello VB 。
我们再来试试字符串。
在form上添加一个按钮TestString 在ModVC中添加函数
Public Function TestString(ByVal s As String) As Long
Rem nothing
End Function
TestString的Click事件中添加如下代码
Private Sub CmdTestString_Click()
Dim s As String
s = Space(256) '为s分配空间
Call TestString(s)
MsgBox s
End Sub
在VC中添加同名函数杰斯提斯 奥特曼
void ModVC::TestString()
{    char* p;
_asm mov p,eax; //取得参数(TextBox的句柄)
memset(p,0,256);//我们知道字符串的长度是256
lstrcpy(p,"VC: Hello VB");
} 编译,生成EXE,运行,点击TestString按钮,。。。咦,显示的是乱码!!??这是因为VB中的String用的是Unicode,VC中复制的不是。转一下就可以了。就在vb中转吧vb有一个函数很方便。把msgbox s 改为 MsgBox StrConv(s, vbUnicode),再生成EXE运行,这次OK了。
接下来我们再试试传递结构体。函数参数格式的声明我们可以参考一下VB中API函数的声明。我来个
简单点的结构体 POINTAPI, 用VB的 API Viewer 到
Public Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long Public Type POINTAPI
x As Long
y As Long
End Type
参照这个我们在ModVC中添加函数
Public Function TestStruct(lppt As POINTAPI) As Long
Rem End Function
在form上添加一个按钮TestStruct,在Click中添加代码
Private Sub cmdTestStruct_Click()
Dim pt As POINTAPI
Call TestStruct(pt)
MsgBox "x= " & pt.x & " y=" & pt.y
End Sub
在VC类中添加同名同类型函数。
void ModVC::TestStruct()
{    POINT *pt;
_asm mov pt,eax;//获取指针参数
pt->x = 123;
pt->y = 456;
} 编译运行。。。。我们会发现非法操作。大概是说引用了0x00000000处的内存。看这个内存地址
我们会知道在vc中引用了一个空指针,那就是说eax的值是0。那则么回事参数没有在eax里面,参数
跑哪里去了?在其它寄存器?不太可能。。。那会在哪里呢,我们总不能凭空变出来一个参数吧。想想C++类中有什么可以用的符号?嗯,有一个 this 指针。会是它吗,熟悉VC的人应该知道在调用类成员函数时会先将该类的this指针存放到 eax 中。看来他们有很大的关系。那就动手试试。函数改为如下:
void ModVC::TestStruct()
{    POINT *pt=(POINT)this;
pt->x = 123;
pt->y = 456;
} 再编译,生成EXE运行。OK!成功了,看到了 x= 123 y=456 .
我们再回过头来看看前面的函数,都假设把this 当着参数试试,经过测试假设成立。如是我们知道this 就是传递过来的参数,当参数是long是,同时eax中也有这个参数值的一个副本。
到现在看来似乎是大功告成了,还没有!还有一个难题,this就只有一个,如果要传递2个,3个,4个。。。参数怎么办呢?
在上一回我们已经实现VB调用VC函数并传递一个long、窗口句柄、字符串指针、结构体指针的参数,并取得了函数的返回值。这一回我们将实践一下如何传递多个参数。
打开上一回的两个工程(LinkWithVC, vcobj). 切换到VB在form上添加一个按钮name: cmdTestStruct2, 标题Test Struct 2。再在ModVC中添加如下代码
Public Function TestArg(lppt1 As POINTAPI, lppt2 As POINTAPI) As Long
Rem
End Function
在cmdTestStruct2的Click事件中
Private Sub cmdTestStruct2_Click()
Dim pt1 As POINTAPI, pt2 As POINTAPI
吃什么可以养胃Call TestArg(pt1, pt2)
MsgBox "x= " & pt1.x & " y=" & pt1.y & vbCrLf & "x= " & pt2.x & " y=" & pt2.y
End Sub
好,现在生成EXE,必然不成功。我们打开log看会是什么错误信息,
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.养老保险领取
Form1.OBJ : error LNK2001: unresolved external symbol "private: void __stdcall ModVC::TestArg(void)" (?TestArg@ModVC@@AAGXXZ)
F:\ : fatal error LNK1120: 1 unresolved externals
我们看到连接器要求的函数声明和无参数,1 个参数的相同,经过测试不能在VB模块声明的函数有多少个参数编译后都
是private: void __stdcall FunctionName(void ) 。
好了我们熟练的在VC的类中加上对应的TestArg函数。编译,生成EXE,通过。下面来解决我们的难
题,现在我们从VC程序的角度来说函数声明是一样的参数个数却是不确定的。这时我们应该会想到C 函数中有一种可变参数的函数,这种函数要求至少有一个确定参数,然后使用一组宏获取其它参数。这里我们先复习一下C语言中可变参数的用法
redcheek/blogview.asp?logID=67
复习完了我们知道函数该怎么写了,如下:
void ModVC::TestArg()
{
LPPOINT lppt1,lppt2;
va_list ap;
lppt1 = (LPPOINT)this;
va_start(ap,this);
lppt2 = va_arg(ap,LPPOINT);
lppt1->x = 123;
厦门旅游必去
lppt1->y = 456;
lppt2->x = 321;
lppt2->y = 654;
}
这样子,能行吗,编译试试;
-------------------Configuration: vcobj - Win32 Release--------------------
<
ModVC.cpp
F:\LinkWithVC\vcobj\ModVC.cpp(72) : error C2102: '&' requires l-va lue
Error

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。