第一 VB中调用VC++开发的DLL
| ||||||||||||||
|
VB和VC混合编程
编号:
QA000072
建立日期 : 1998 年 11 月 4 日 最后修改日期: 2001 年 2 月 8 日 所属类别: ![]() 本人初学 VC5 ,现与几个同事合作开发单位课题( WIN95 应用程序)。我负责数据计算,并将计算结果以图形的方式显示。由于计算的复杂性,也为提高速度,我准备用 VC5 编程,但伙伴们却用 VB5 开发主界面。请指教: 我如何从 VB5 程序中获取数据(数据量很大,且对传输速度要求很高),我又如何接管伙伴用 VB5 生成的窗口,在不开新窗口,不破坏主界面的前提下,将图形输出到视窗的指定位置,并进行一系列的图形操作;我应生成什么类型的文件,伙伴在 VB5 中如何调用我的程序?能否举例说明? ![]()
一般来说,
VB
和
VC
共同编程有
3
种方式:一种是
VC
生成
DLL
,在
VB
中调用
DLL
;一种是
VC
生成
ActiveX
控件
(.ocx)
,在
VB
中插入;还有一种是在
VC
中生成
ActiveX Automation
服务器,在
VB
中调用。相对而言,第一种方法对
VC
编程者的要求最低,但要求你的伙伴进行配合,我推荐这种方法。
先说说 VC++ 的编程。首先在 VC++ 中生成 Win32 DLL 工程。在这个工程中添加几个函数供 VB 用户调用。一个 DLL 中的函数要想被 VB 调用,必须满足两个条件:一是调用方式为 stdcall ,另一个是必须是 export 的。要做到第一条,只须在函数声明前加上 __stdcall 关键字。如 : short __stdcall sample(short nLen, short *buffer) 要做到第二条,需要在 *.def 文件中加上如下的几行: EXPORTS sample @1 这里的 sample 是你要在 VB 中调用的函数名, @1 表示该函数在 DLL 中的编号,每个函数都不一样。注意这里的函数名是区分大小写的。至于你说的需要传递大量数据,可以这样做,在 VB 中用一个数组存放数据,然后将该数组的大小和地址传给 VC( 至于如何在 VB 中编程我会在下面介绍 ) 。就象上面的例子, nLen 是数组大小, buffer 是数组地址,有了这两条,你可以象使用 VC 的数组一样进行处理了。至于输出图形,可以生成 WMF 或 BMP 格式,让 VB 调用。不过,我认为也可以直接输出到视窗,只要 VB 将窗口的句柄 hWnd 和 hDC 以及视窗的绘图位置 (VB 和 VC 采用的坐标系必须一致才行 ) 传给 VC 就行了。而 VB 的 AutoRedraw 属性必须为 False ,在 Paint 事件中调用 VC 的绘图程序。 再谈谈 VB 的编程。 VB 调用 DLL 的方法和调用 Windows API 的方法是一样的,一般在 VB 的书中有介绍。对于上面一个例子,先要声明 VC 函数: Declare Function sample Lib "mydll.dll" (ByVal nLen As Integer, buffer As Integer) As Integer 这里 mydll.dll 是你的 dll 的名字。你可能已经注意到了两个参数的声明有所不同,第一个参数加上了 ByVal 。规则是这样的:如果在 VC 中某个参数声明为指针和数组,就不加 ByVal ,否则都要加上 ByVal 。在 VB 中调用这个函数采用这样的语法: sample 10, a(0) 这里的 a() 数组是用来存放数据的, 10 为数组长度,这里的第二个参数不能是 a() ,而必须是要传递的数据中的第一个。这是 VB 编程的关键。 下面在说几个可能遇到的问题。一个问题是 VB 可能报告找不到 dll ,你可以把 dll 放到 system 目录下,并确保 VB 的 Declare 语句正确。另一个问题是 VB 报告找不到需要的函数,这通常是因为在 VC 中 *.def 文件没设置。第三种情况是 VB 告诉不能进行转换,这可能是在 VC 中没有加上 __stdcall 关键字,也可能是 VB 和 VC 的参数类型不一致,注意在 VC 中 int 是 4 个字节 ( 相当于 VB 的 Long) ,而 VB 的 Integer 只有 2 个字节。必须保证 VB 和 VC 的参数个数相同,所占字节数也一致。最后一个要注意的问题是 VC 中绝对不能出现数组越界的情况,否则会导致 VB 程序崩溃。 总的来说,你和你的伙伴需要一些时间来进行协调和摸索,但这种方法绝对可行,也不难掌握。 相关问题: QA000981 "用VC写的程序中调用用VB写的函数和方法" ![]() 您回答了在 VB 中调用 DLL 文件中的函数问题,如果我想在 VB 中调用 DLL 文件中所定义的类,那么该如何做呢?拜托!!! 答: 如果你说的 DLL 的类是 Automation 方式的,只要在 VB 的 “ 引用 ” 对话框中添加该 DLL 就可以。但是如果该类是在 VC++ 中以 dllexport 形式定义的,则除了 VC++ ,其他语言都无法调用。如果你想让 VB 以 Declare 的方式调用 C++ 的类,你需要改写 DLL 为普通 C 语言函数的形式, DLL 内部可以使用类,但对外的调用接口必须是简单的函数。 ![]() 具体范例,请到纪文和网站 VB 入门网 http://www.vbguide.com.tw/webback.asp 看看「个个击破」 单元中的 --------------------------------------- 问题 389 如何解一元二次方程式 (Quadratic Equation of One Variable) --------------------------------------- 1) 请先阅读前言之说明。 2) 若适合现状者可以下载范例,研究程序写法。 3) 参考其标示之『参考资料』及『网络资源』。 |
|
第二 调用中注意
在
VB
定义中注意:
函数如果有返回值定义为
FUNCTION
如果没有返回值定义为
SUB
否则也会报
Bad DLL calling convention
错误
|
一个成功的例子
WordCutDllProject.cpp
// WordCutDllProject.cpp : Defines the initialization routines for the DLL.
//
#include "stdafx.h"
#include "WordCutDllProject.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
extern "C" void _stdcall HelloWorld();
//
//
Note!
//
//
If this DLL is dynamically linked against the MFC
//
DLLs, any functions exported from this DLL which
//
call into MFC must have the AFX_MANAGE_STATE macro
//
added at the very beginning of the function.
//
//
For example:
//
//
extern "C" BOOL PASCAL EXPORT ExportedFunction()
//
{
//
AFX_MANAGE_STATE(AfxGetStaticModuleState());
//
// normal function body here
//
}
//
//
It is very important that this macro appear in each
//
function, prior to any calls into MFC. This means that
//
it must appear as the first statement within the
//
function, even before any object variable declarations
//
as their constructors may generate calls into the MFC
//
DLL.
//
//
Please see MFC Technical Notes 33 and 58 for additional
//
details.
//
/
// CWordCutDllProjectApp
BEGIN_MESSAGE_MAP(CWordCutDllProjectApp, CWinApp)
//{{AFX_MSG_MAP(CWordCutDllProjectApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CWordCutDllProjectApp construction
CWordCutDllProjectApp::CWordCutDllProjectApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
void __stdcall HelloWorld()
{
AfxMessageBox("HelloWorld!");
}
/
// The one and only CWordCutDllProjectApp object
CWordCutDllProjectApp theApp;
|
WordCutDllProject.def
; WordCutDllProject.def : Declares the module parameters for the DLL.
LIBRARY "WordCutDllProject"
DESCRIPTION 'WordCutDllProject Windows Dynamic Link Library'
EXPORTS
; Explicit exports can go here
HelloWorld @1
|
Vb 代码
Private Declare Sub HelloWorld Lib "D:/VBPROJECT/GeoCodingBatchMatchProject/GeoCodingBatchMatchProject/GeoCodingBatchMatchProject/bin/Debug/WordCutDllProject.dll" ()
Private Sub Command1_Click()
HelloWorld
End Sub
|
VB
与
VC
通信初探(二)
日期:2005-06-23人气: 560 出处:优快云 作者: eliner
在通过
DLL
实现
VB
与
VC
的过程中,一般的简单的通信过程很容易建立。在《
VB
与
VC
通信初探(一)》中,我已经把基本的通信过程给描述了一下,但是,有些时候,事件的发展并不总是一帆风顺的,当我们以为可以轻松的实现
VB
与
VC
的通信的时候,就经常发生这样的情况,还是先看看例子再说。
VC
中的程序:
extern "C" _declspec(dllexport) bool ReadString(LPCSTR lpString)
{
char str[]="Hello World!";
if(strcmp(lpString,str)==0)
return true ;
else
return false ;
}
VB
中的程序:
Private Declare Function ReadString Lib "commication.dll" (ByVal send As String) as Boolean
……………………
Dim result As Boolean
Dim send As String
send =”Hello,World!”
result = ReadString(send)
If result Then
MsgBox "The return value is 'true'"
Else
MsgBox "The return value is 'false'"
End If
可以上机运行这个程序,一样的,实现了通信的功能,但是这里有几个地方是应该引起我们注意的,首先就是在
VB
中声明传递的字符串参数的时候,按照常规的理解,好像应该是传递地址才对,但是这里使用的方法是
ByVal
,为什么?原因比较复杂,但是可以简单的来分析一下,在
VB
中,使用的字符串实际上是
BSTR
类型的,
它是由自动化(以前被称为
OLE Automation
)定义的数据类型。一个
BSTR
由头部和字符串组成,头部包含了字符串的长度信息,字符串中可以包含嵌入的
null
值。大部分的
BSTR
是
Unicode
的,即每个字符需要两个字节。
BSTR
通常以两字节的两个
null
字符结束。下图表示
了一个
BSTR
类型的字符串。
(前缀) aTest/0 头部 BSTR 指向数据的第一个字节 另一方面,大部分的 DLL 过程(包括 Windows 95 API 中的所有过程)使用 LPSTR 类型字符串,这是指向标准的以 null 结束的 C 语言字符串的指针,它也被称为 ASCIIZ 字符串。 LPSTR 没有前缀。下图显示了一个指向 ASCIIZ 字符串的 LPSTR 。 aTest/0 LPSTR 指向一个以 null 结尾的字符串数据的第一个字节 通过上面的简单分析,不难看出,如果均以地址的方式传递参数的话,那么 VB 中的字符串将会包含更多德内容,所以,在这里,就必须以值的方式传递参数,虽然是以值得方式传递的参数,但是在 DLL 中还是能够识别得出来这是一个字符串,并且将它转换成为字符串。
好了,上面的这个问题我们已经解决了,但是,我们现在的胃口肯定也变得慢慢得有点大了,既然我们已经实现了能够从
VB
中把字符串传给
DLL
,那么,又应该怎么样才能够从
DLL
中把字符串返回给
VB
程序呢?通过上面的分析我们知道,因为两者之间使用的字符不是相同的格式的,所以简单的传输肯定是不行的,那么应该如何解决呢?其实,在明白了上面我们分析的道理后,再来解决这个问题就太
easy
了,只需要把传回的字符串进行一次转换就可以了,是的,下面就给出这个实例,注意的黑体部分就可以了。
VC
中的程序:
extern "C" _declspec(dllexport) BSTR ReadString(LPCSTR lpString)
{
char str="Hello ,World!";
if(strcmp(lpString,str)==0)
return SysAllocString((BSTR)str);
else
return SysAllocString((BSTR)lpString);
}
VB
中的程序:
Private Declare Function ReadString Lib "commication.dll" (ByVal send As String) as String
……………………
Dim result As String
Dim send As String
send =”Hello,World!”
result = ReadString(send)
MsgBox result
为了继续学习下去,我一定会继续完成这一系列的文章,同时也希望可以看到愿意一起学习这方面的知识的朋友给我提意见,与我联系,共同进步!
|
第三 VB。NET中程序的退出
触发关闭窗口:
Process.GetCurrentProcess().CloseMainWindow()
当窗口关闭后无法关闭进程,在
MainForm_FormClosed事件中强制关闭进程
Process.GetCurrentProcess().Close()
If Not Process.GetCurrentProcess().HasExited Then
Process.GetCurrentProcess().Kill()
End If
|
第四 关于dll生成的问题
在发布打包时一定要用release
方式(不能是debug
模式)生成的dll
,否则在没有vc
开发环境下,运行是有问题的。