vb调用VC动态连接库的总结

VS类 专栏收录该内容
10 篇文章 0 订阅
 
第一 VB中调用VC++开发的DLL
 
再谈在 VB 中调用 VC++ 开发的 DLL
 
 
 
 
 
 
[ 来源: xue5.com | 作者:学网 | 时间: 2005-5-8 ]
 
 

 共有 133[学网]的网友和你一起阅读过:再谈在VB中调用VC++开发的DLL

 
 
 
 
 
 
 
 
   近日开发一个程序,用到动态链接库,在 VB 中调用时遇到了一些问题。我查了一些资料,也看了一下 CSDN 上的文章,感觉这些文章对在 VB 中调用 VC++ 开发的 DLL 这一问题阐述得不够详细。因此在我的问题得到解决之余,特为初接触 DLL 的朋友们写下这篇文章。
本文中关于调用约定的解决方法,也适用于解决其它编程语言之间 DLL 调用的兼容问题。
 
  ①关于 DLL 的创建与调用
  使用 VC++ 的向导即可。具体操作如下:打开菜单“ File/New ”→选择“ Projects/Win32 Dynamic-Link Library ”→选择“ A simple DLL project ”即可。这时系统会自动生成 3 个文件: *.cpp stdafx.cpp stdafx.h
  之后将入口函数 DLLMain() 补充完整,再添加你自定义的函数的代码。如果你自定义的函数很多,可以将这些函数的声明部分统一写入一个头文件中。再在 .cpp 文件首部用“ #include ”语句引入这个头文件。注意函数声明前要加上“ __declspec(dllexport) ”。
  (如果你建 DLL 时选择的是第三种类型 ( 加入示例代码 ) ,则在函数声明及定义前都要加上系统定义的宏“ *_API ”。)
  在 VB 中用如下语句声明:“ Declare Function 函数名 Lib " 完整路径 / 文件名 .dll" [Alias " 函数别名 "] (ByVal 变量 1 As 类型 1, ByVal 变量 2 As 类型 2, ) As 类型 3 ,与调用 API 函数类似。
  注意:若在窗体代码的“通用”部分使用,“ Declare ”前要加“ Private ”;若在 Moudle 中使用,“ Declare ”前要加“ Public ”。若将 DLL 文件放在“ /Windows/System ”或“ /WinNT/System32 ”目录下,“ Lib ”后只写出 DLL 主文件名即可。
具体的实例代码见④(修正后的,可直接运行)。 
 
  ②关于入口点
  如上编写 Cipher.dll ,运行,出现错误信息“找不到 DLL 入口点 (Error 53) ”。出现这一错误的原因是 C++ 编译器在编译时对函数名 Encrypt 作了修改。打开快速查看程序( D:/WINNT/System32/Viewers/QuikView.exe , Cipher.dll 拖入查看窗口,找到字段 ?Encrypt@@YAHHH@Z , 发现函数名被加了一串字符。
  解决方法有二。第一,直接在 VB 声明中将“ ?Encrypt@@Y AHHH@Z ”作为别名放在“ Alias ”后即可;第二,在 Cipher.dll 代码中在语句“ __declspec(dllexport) int __stdcall Encrypt(int p, int k); ” 前加上“ extern "C" ” ,编译后,用 QuikView 查看,函数名变为“ _Encrypt ”,之后再在 VB 声明中做相应调整即可。
  (对于使用宏的 DLL ,在“ #define ”语句中,对宏“ Cipher_API ”的替换值做更改即可。)
进行了③的更改后,程序又找不到入口点了。再用 QuikView 查看,发现函数名变为 _Encrypt@8 ”。还有解决方法。在 Cipher.dll 工程中添加一个文本文件,命名为“ Cipher.def , 添加代码如④。编译后再用 QuikView 查看,函数名变回“ Encrypt ”,在 VB 中调用,运行正常。
 
  ③关于调用约定
  采用②中第二种解决方法,运行,出现错误信息“ DLL 调用约定错误 (Error 49) ”。原因是调用约定共有 4 种方式: __fastcall __pascal __stdcall __cdecl VC++ 默认调用方式为 __cdecl ,而 VB 默认调用方式则为 __stdcall 。解决方法是,更改代码如下(限定调用方式):
   extern “C” __declspec(dllexport) int __stdcall Encrypt( int p, int k);
  …………
   int __stdcall Encrypt( int p, int k)
    {
           int c = p+k;
           return c;
    }
 
  ④源代码
Cipher.dll
  Cipher.cpp
    // 引入预编译头文件
   #include “stdafx.h”
    // 声明我的函数
extern “C” __declspec(dllexport) int __stdcall Encrypt( int p, int k );
    //DLL 入口函数
       BOOL APIENTRY DllMain( HANDLE  hModule,
                                                   DWORD  ul_reason_for_call,
                                                   LPVOID  lpReserved
                                                  )
       {
           switch ( ul_reason_for_call )
           {
             case  DLL_PROCESS_ATTACH:
             case  DLL_THREAD_ATTACH:
             case  DLL_THREAD_DETACH:
             case  DLL_PROCESS_DETACH:
                     break ;
           }
           return TRUE;
       }
 
        // 我的函数
        int __stdcall Encrypt ( int p, int k )
        {
            int c = p + k ;
            return c ;
        }
 
  Cipher.def
   LIBRARY Cipher
   EXPORTS Encrypt
 
编译后,将 Cipher.dll 复制到“ D:/WINNT/System32”目录。
VB 中调用:
   Option Explicit
Private Declare Function Encrypt Lib “Cipher” _
( ByVal p As Long, ByVal k As Long) As Long
        Private Sub Form_Load()
   Dim c As Long
   c = Encrypt(24, 8)
   Text1.Text = c
   End Sub
 
 
 
 
 
 
 
VBVC混合编程
 
编号: 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 内部可以使用类,但对外的调用接口必须是简单的函数。
    
     spencer yang
的意见:
    
具体范例,请到纪文和网站 VB 入门网
    http://www.vbguide.com.tw/webback.asp
    
看看「个个击破」 单元中的
    ---------------------------------------
    
问题 389 如何解一元二次方程式 (Quadratic Equation of One Variable)
    ---------------------------------------
    1)
请先阅读前言之说明。
    2)
若适合现状者可以下载范例,研究程序写法。
    3)
参考其标示之『参考资料』及『网络资源』。
 
 
 
VB 中调用 DLL 显示 Bad DLL calling convention
编号:
QA001383
 
 
建立日期:
1999 7 22
最后修改日期:
2005 8 22
所属类别:
 
 
     使用的操作系统 :Win95/98
    
使用的编程工具 :VB6 VC6
    
遇到的问题 : 鉴于 VB 的速度和在位操作方面的不足,我不得不用 VC 来编写一个 DLL 来实现一些功能。可在 VB 中调用是总是说 "Bad DLL calling convention"
    
VC 中的源程序如下 :( 一个极其简单的试验用的函数 )
    #define DllExport __declspec( dllexport )
    
    DllExport int Gen(int a)
    {
     return 2;
    }
    
    DllExport int Gen2(void)
    {
     return 1;
    }
    
    
VB 中声明如下 :
    Declare Function Gen Lib "Paper.dll" (ByVal a As Integer) As Integer
    Declare Function Gen2 Lib "Paper.dll" () As Integer
    
调用程序 :
    
    Dim SS As Integer
     SS = 1
     SS= Gen2() //
调用成功
     SS = Gen(SS) //
一到这就报错 ,?????????(Stone)
 
 
     产生这条错误有几个原因:
    1
、参数声明错误, C 语言的 int 型对应为 VB Long 型。在 VB 中声明如下 :
     Declare Function Gen Lib "Paper.dll" (ByVal a As long) As long
     Declare Function Gen2 Lib "Paper.dll" () As long
    2
DLL 中的函数没有使用 stdcall 调用方式。
    VB
的错误信息指的是第二条原因。你定义的调用方式为 __declspec ,而 VB 要求必须是 stdcall 方式,这对没有参数的函数影响不大,但对有参数的函数有影响。你可以参照 QA000072 "VBVC混合编程" 修改你的 VC 程序。
 
第二 调用中注意
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 通信初探(二)
】【 打印 】【 加入收藏 】【 关闭 收藏到新浪ViVi 】【 收藏到365KEY 浏览字号 :

日期:2005-06-23人气: 560 出处:CSDN 作者: 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 开发环境下,运行是有问题的。
 
  • 1
    点赞
  • 0
    评论
  • 1
    收藏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值