精华内容
下载资源
问答
  • vb调用dll的各种方法

    2019-01-20 00:53:56
    vb调用dll的各种方法,便于开发VB的高级编程。 为广大编程爱好者,方便提高编程水平。
  • VB 调用WINRAR的dll实例

    2015-07-09 22:35:12
    VB 调用WINRAR的unrar.dll实例
  • VB制作DLL并引用DLL实例源码

    热门讨论 2012-08-09 11:22:26
     写exe代码(包含调用DLL代码)  生成usedll.exe,usedll.exe和mydll.DLL要放在一个目录下。    注意:如果引用的时候可能会出现这样的错误:“名称与已存在的工程模块对象冲突”  解决...
  • VS2010使用VB.NET调用C++的DLL例子, 内含一个C++范例程序和一个VB.NET调用范例。
  • VB调用dll的应用实例

    2014-05-02 17:31:40
    这个方法是从我的毕业设计中截取的,里面除了详细介绍VB调用dll的步骤和过程外,还包含了把matlab的m文件编译成dll的详细过程。因此,适合VB与matlab混合编程使用。 这种方法是经过我实际验证的,绝对可行。之前也...
  • VBA调用dll实例

    2010-08-12 18:58:37
    VBA调用dll是指定路径的,如果该路径下没有想调的dll怎么办,本文给出了一个解决方法
  • vb.net编写dll给其他语言调用源码.rar 1.如何写一个dll给其他语言或其他程序调用。这里有个简单的实例 2.如果真好想在写dll而不是exe,可以下载下来学习,看看如何写ddl及调用的。 3.编译出来的dll不仅仅是给其他...
  • VB写DLL和调用dll实例

    热门讨论 2008-09-05 19:05:50
    这个实例,展示了VB调用Dll,vb写DLL(只要在"工程"->"引用"->引用进去就可以了)
  • VS2010 VB如何生成DLL文件,调用DLL 详细过程参看http://blog.csdn.net/zzuzpb/article/details/9838541
  • vb调用VC DLL 实例

    2009-06-24 15:34:28
    vb调用VC DLL 实例 希望对初学者有用。 包括vc写的dll vb 调用,有源代码。
  • VB6.0调用DLL

    千次阅读 2016-12-10 16:25:56
    第1章 VB6.0调用DLL 1 1 VC++编写DLL 1 1.1 使用__stdcall 1 1.2 使用 .DEF 文件 1 2 简单数据类型 2 2.1 传值(ByVal) 2 2.2 传址(ByRef) 3 2.3 传址(VarPtr) 4 2.4

     

    1 VB6.0调用DLL    1

    1 VC++编写DLL    1

    1.1 使用__stdcall    1

    1.2 使用 .DEF 文件    1

    2 简单数据类型    2

    2.1 传值(ByVal    2

    2.2 传址(ByRef    3

    2.3 传址(VarPtr    4

    2.4 转换为Variant    4

    3 String    6

    3.1 BSTR内存布局    6

    3.2 StrPtrVarPtr    7

    3.3 示例代码    7

    3.4 转换为Variant    12

    3.5 小结    13

    4 结构    14

    4.1 定义    14

    4.2 示例代码    14

    5 数组    17

    5.1 简单数据类型数组    17

    5.2 转换为Variant    18

    5.3 结构数组    19

    6 Object    21

    7 函数    21

     

     

    1 VB6.0调用DLL

    1 VC++编写DLL

    VB6.0VBA可以调用DLL里的导出函数。可以使用VC++编写DLL,供VB6.0调用。VC++编写DLL时,需要注意以下几点:

    1.1 使用__stdcall

    C函数在默认情况下,其调用约定为__cdecl。为了让VB6.0调用此函数,需要特别设置调用约定为__stdcall(或 WINAPI)。如下面的C代码:

    void __stdcall Test()

    {

    }

    1.2 使用 .DEF 文件

    使用VC++编写DLL,可以使用 __declspec(dllexport) 导出一个函数。如:

    __declspec(dllexport) void __stdcall Test()

    {

    }

    使用VC++6.0C编译器编译上述代码,则导出函数名为_Test@0。此时,VB6.0声明Test函数的代码如下:

    Private Declare Sub Test Lib "Test.dll" Alias "_Test@0" ()

    "Alias "_Test@0""明确说明了导出函数的名称。

    为了简化VB6.0Test函数的声明,可使用DEF文件,具体如下:

    1、使用DEF文件。下面是DEF文件的内容,它表示导出Test函数:

    EXPORTS

    Test 

    2VC代码里不使用__declspec(dllexport),如下面的代码

    void __stdcall Test()

    {

    }

    3VB6.0声明Test函数时无需Alias,如下面的声明

    Private Declare Sub Test Lib "Test.dll" ()

    2 简单数据类型

    VB6.0中,常见的数据类型请见下表

    VB6.0

    C

    Boolean

    VARIANT_BOOL

    就是short

    VARIANT_TRUE    等于-1表示 TRUE

    VARIANT_FALSE    等于0表示 FALSE

    Byte 

    BYTE

    就是unsigned char

    Currency

    CY

    CURRENCY

    就是 __int64

    如:1234567表示货币值123.4567@

    Date 

    DATE

    就是double

    表示18991230日到当前时刻的天数,对应于MFCCOleDateTime

    Decimal

    DECIMAL

    VB6.0DecimalVariant表示

    Double

    double 

     

    Integer

    short 

     

    Long

    long 

     

    Single

    float 

     

    String

    BSTR 

    Unicode字符串

    Variant

    VARIANT

     

    上表中,除了DecimalStringVariant之外其它均为简单数据类型。

    VB6.0传递简单数据类型给DLL时,分为传值和传址两种方式:

    2.1 传值(ByVal

    VC++代码:

    long WINAPI TestSimpleV(VARIANT_BOOL b,BYTE bt,__int64 c

    ,DATE dt,double d,short i,long l,float s)

    {

    b = VARIANT_TRUE; //改变形参,不会影响VB6.0里的实参

    return 0;

    }

    VB6.0声明代码:

    Public Declare Function TestSimpleV Lib "Test.dll" _

    (ByVal b As Boolean, ByVal bt As Byte, ByVal c As Currency, _

    ByVal dt As Date, ByVal d As Double, ByVal i As Integer, _

    ByVal l As Long, ByVal s As Single) As Long

    VB6.0调用代码:

    Dim b As Boolean,bt As Byte,c As Currency,dt As Date

    Dim d As Double,i As Integer,l As Long,s As Single

    Dim nRet As Long

    nRet = TestSimpleV(b, bt, c, dt, d, i, l, s)

    说明:

    1VB6.0声明函数时,必须使用ByVal,表示把实参的数值传递给形参;

    2、因为传递的是数值,所以VC函数里改变形参的数值不会影响到实参。

    2.2 传址(ByRef

    VC++代码:

    long WINAPI TestSimpleR(VARIANT_BOOL*b,BYTE*bt,__int64*c

    ,DATE*dt,double*d,short*i,long*l,float*s)

    {

    if(b)     { *b    = VARIANT_TRUE;     }

    if(bt) { *bt    = 2;             }

    if(c)     { *c    = 654321;        } //货币值 65.4321@

    if(d)     { *d    = 3.4;             }

    if(i)     { *i    = 5;             }

    if(l)     { *l    = 6;             }

    if(s)     { *s    = 7.8f;             }

    if(dt)

    {

    SYSTEMTIME tmSys;

    GetLocalTime(&tmSys);

    SystemTimeToVariantTime(&tmSys,dt);

    }

    return 1;

    }

    VB6.0声明代码:

    Public Declare Function TestSimpleR Lib "Test.dll" _

    (ByRef b As Boolean, ByRef bt As Byte, ByRef c As Currency, _

    ByRef dt As Date, ByRef d As Double, ByRef i As Integer, _

    ByRef l As Long, ByRef s As Single) As Long

    VB6.0调用代码:

    Dim b As Boolean, bt As Byte, c As Currency, dt As Date

    Dim d As Double, i As Integer, l As Long, s As Single

    Dim nRet As Long

    nRet = TestSimpleR(b, bt, c, dt, d, i, l, s)

    说明:

    1VB6.0声明函数时,应使用ByRef,表示把实参的地址传递给形参;

    2、因为传递的是地址,所以VC函数里改变形参的数值将影响到实参。

    2.3 传址(VarPtr

    使用ByRef无法将NULL指针传递给DLL,为此可使用函数VarPtr。示例代码如下:

    VB6.0声明代码:

    Public Declare Function TestSimpleP Lib "Test.dll" Alias "TestSimpleR" _

    (ByVal b As Long, ByVal bt As Long, ByVal c As Long, _

    ByVal dt As Long, ByVal d As Long, ByVal i As Long, _

    ByVal l As Long, ByVal s As Long) As Long

    VB6.0调用代码:

    Dim b As Boolean,bt As Byte,c As Currency,dt As Date

    Dim d As Double,i As Integer,l As Long,s As Single

    Dim nRet As Long

    nRet = TestSimpleP(VarPtr(b), VarPtr(bt), VarPtr(c), VarPtr(dt), _

    VarPtr(d), VarPtr(i), VarPtr(l), VarPtr(s))

    VarPtr(b)将返回变量b的地址,被VB6.0当做long传递给DLL。若要传递NULL指针,可修改VarPtr(...)0

    VB.NET不支持VarPtr函数,下面是从网上找到的代码,不知是否稳定。

    Public Function VarPtr(ByVal e As Object) As Integer

    Dim GC As GCHandle = GCHandle.Alloc(e, GCHandleType.Pinned)

    Dim GC2 As Integer = GC.AddrOfPinnedObject.ToInt32

    GC.Free()

    Return GC2

    End Function

    2.4 转换为Variant

    VB6.0里可以将简单数据类型转换为Variant,然后再传递给DLL。下面是示例代码:

    VC++代码:

    void WINAPI TestSimpleVariant(VARIANT v,VARIANT*r)

    {

    if(v.vt == VT_R8)

    {

    v.dblVal = -1.2;     //不会影响实参

    }

    if(r && r->vt == (VT_R4 | VT_BYREF))

    {

    *r->pfltVal = -3.4f;     //会影响实参

    }

    }

    VB6.0声明代码:

    Public Declare Sub TestSimpleVariant Lib "Test.dll" _

    (ByVal v As Variant, ByRef r As Variant)

    VB6.0调用代码:

    Dim d As Double, s As Single

    d = 1.2

    s = 3.4!

    MsgBox Hex$(VarPtr(d)) & " = " & d & vbCrLf & _

    Hex$(VarPtr(s)) & " = " & s

    Call TestSimpleVariant(d, s)

    MsgBox Hex$(VarPtr(d)) & " = " & d & vbCrLf & _

    Hex$(VarPtr(s)) & " = " & s

    运行VB6.0代码,将依次出现:

    1、首先显示如下界面

    说明:变量d的首地址为0x18F374,数值为1.2;变量s的首地址为0x18F370,数值为3.4

    2VB6.0 ds 转换为Variant,然后传递给VC函数TestSimpleVariant。注意:r->pfltVal就是实参s的首地址;

    3VB6.0Call TestSimpleVariant(d, s)执行完毕后,d的数值未变,s的数值被改变,ds的地址均未改变,如下图所示:

     

    3 String

    可以认为VB6.0String就是C语言的BSTRBSTR可以表示宽字符串(Unicode),也可以表示窄字符串(ANSI)。在VB6.0EVB3.0里,String对应的BSTR是一个宽字符串。

    3.1 BSTR内存布局

    BSTR s表示了一个字符串。s是一个指针,指向了一个ANSIUNICODE字符串。此外,BSTR还包含了字符串的长度信息。具体结构如下:

    字符串所占字节数

    4字节

    字符串

    首地址就是s

    结束符

    如:字符串"123"以ANSI的BSTR表示(假定s的值为0x1000)

    地址

    内容

    0x0FFC

    03H

    字符串所占字节数,这里为3

    0x0FFD

    00H

    0x0FFE

    00H

    0x0FFF

    00H

    0x1000

    31H

    字符串123

    0x1001

    32H

    0x1002

    33H

    0x1003

    00H

    结束符

    字符串"123"以UNICODE的BSTR表示(假定s的值为0x1000)

    地址

    内容

    0x0FFC

    06H

    字符串所占字节数,这里为6

    0x0FFD

    00H

    0x0FFE

    00H

    0x0FFF

    00H

    0x1000

    31H

    字符串123

    0x1001

    00H

    0x1002

    32H

    0x1003

    00H

    0x1004

    33H

    0x1005

    00H

    0x1006

    00H

    结束符

    0x1007

    00H

    所以,当BSTR s表示ANSI字符串时,可以把s当作char*;当BSTR s表示UNICODE字符串时,可以把s当作wchar_t*

    3.2 StrPtrVarPtr

    StrPtr返回的是字符串首字符的地址(可看做wchar_t*),VarPtr返回的是字符串变量的地址(可看做wchar_t**)。参考下表,可以更好的理解:

    VB6.0代码

    C代码

    Dim s As String

    BSTR s;

    Dim n As Long

     

    n = StrPtr(s)

    wchar_t*n = (wchar_t*)s;

    n = VarPtr(s)

    wchar_t**n = (wchar_t**)&s;

    3.3 示例代码

    VB6.0里将String变量传递给DLL的示例代码如下:

    VC++代码:

    BSTR WINAPI TestString(BSTR v,BSTR*r,BSTR sp,BSTR*vp)

    {

    if(v)

    {//VB6.0 传过来 vbNullString,则 v NULL

    char*p = (char*)v; //ANSI 字符串首地址

    UINT& nLen = ((UINT*)p)[-1]; //ANSI 字符串长度

    if(nLen > 0)

    {

    p[0] = 'v'; //允许修改字符串

    nLen = 1; //允许将字符串长度变小

    p[nLen] = '\0'; //修改字符串长度后,这句是必需的

    }

    }

    {

    char* p = *(char**)r; //ANSI 字符串首地址

    if(p)

    {//VB6.0 传过来 vbNullString,则 r 不为 NULLp NULL

    UINT&nLen = ((UINT*)p)[-1]; //ANSI 字符串长度

    if(nLen > 0)

    {

    p[0] = 'r'; //允许修改字符串

    nLen = 1; //允许将字符串长度变小

    p[nLen] = '\0'; //修改字符串长度后,这句是必需的

    }

    }

    else

    {//重新定义字符串

    char*szStr    =    "C 函数生成的字符串";

    SysFreeString(*r);            //释放以前的字符串

    *r = SysAllocStringByteLen(szStr,strlen(szStr)); //修改字符串

    }

    }

    {

    wchar_t*p = (wchar_t*)sp; //Unicode 字符串首地址

    if(p)

    {//VB6.0 传过来 vbNullString,则 sp 不为 NULLp NULL

    UINT&nLen2 = ((UINT*)p)[-1]; //Unicode 字符串字节数

    if(nLen2 >= 2)

    {

    p[0] = L's'; //允许修改字符串

    nLen2 = 2; //允许将字符串长度变小

    p[nLen2>>1] = L'\0'; //修改字符串长度后,这句是必需的

    }

    }

    }

    {

    wchar_t*p = *(wchar_t**)vp; //Unicode 字符串首地址

    if(p)

    {//VB6.0 传过来 vbNullString,则 vp 不为 NULLp NULL

    UINT&nLen2 = ((UINT*)p)[-1]; //Unicode 字符串字节数

    if(nLen2 >= 2)

    {

    p[0] = L'v'; //允许修改字符串

    nLen2 = 2; //允许将字符串长度变小

    p[nLen2>>1] = L'\0'; //修改字符串长度后,这句是必需的

    }

    }

    }

    {//返回值,允许返回 NULL

    char*szStr    =    "C 函数返回的字符串";

    return SysAllocStringByteLen(szStr,strlen(szStr));

    }

    }

    VB6.0声明代码:

    Public Declare Function TestString Lib "Test.dll" _

    (ByVal v As String, ByRef r As String, _

    ByVal sp As Long, ByVal vp As Long) As String

    VB6.0调用代码:

    Dim v As String

    Dim r As String

    Dim sp As String

    Dim vp As String

    v = txtV.Text

    r = txtR.Text

    sp = txtSP.Text

    vp = txtVP.Text

    txtReturn.Text = TestString(v, r, StrPtr(sp), VarPtr(vp))

    txtV.Text = v

    txtR.Text = r

    txtSP.Text = sp

    txtVP.Text = vp

    下面是对上述代码的详细说明:

    3.3.1 ByVal v As String

    VB6.0里的变量v是一个String,其实就是一个UnicodeBSTR

    调用TestString之前,VB6.0会将v转换为AnsiBSTR,并将这个BSTR传递给DLL

    DLL的处理代码如下:

    if(v)

    {//VB6.0 传过来 vbNullString,则 v NULL

    char*p = (char*)v; //ANSI 字符串首地址

    UINT& nLen = ((UINT*)p)[-1]; //ANSI 字符串长度

    if(nLen > 0)

    {

    p[0] = 'v'; //允许修改字符串

    nLen = 1; //允许将字符串长度变小

    p[nLen] = '\0'; //修改字符串长度后,这句是必需的

    }

    }

    首先要判断v是否为NULL,因为VB6.0传递字符串vbNullString时,该参数将为NULL。接下来获得ANSI字符串的首地址p和长度nLen。允许修改ANSI字符串的内容和长度,但是不能加长字符串。

    VC++TestString函数返回后,VB6.0会将ANSI字符串转换为Unicode字符串,并由此改变实参v

    3.3.2 ByRef r As String

    VB6.0里的变量r是一个String,其实就是一个UnicodeBSTR

    调用TestString之前,VB6.0会将r转换为AnsiBSTR,并将这个BSTR传递给DLL

    DLL的处理代码如下:

    {

    char* p = *(char**)r; //ANSI 字符串首地址

    if(p)

    {//VB6.0 传过来 vbNullString,则 r 不为 NULLp NULL

    UINT&nLen = ((UINT*)p)[-1]; //ANSI 字符串长度

    if(nLen > 0)

    {

    p[0] = 'r'; //允许修改字符串

    nLen = 1; //允许将字符串长度变小

    p[nLen] = '\0'; //修改字符串长度后,这句是必需的

    }

    }

    else

    {//重新定义字符串

    char*szStr    =    "C 函数生成的字符串";

    SysFreeString(*r);            //释放以前的字符串

    *r = SysAllocStringByteLen(szStr,strlen(szStr)); //修改字符串

    }

    }

    即:可以修改ANSI字符串内容和长度,也可以重新生成字符串。

    VC++TestString函数返回后,VB6.0会将ANSI字符串转换为Unicode字符串,并由此改变实参r

    3.3.3 ByVal sp As Long

    VB6.0里的变量sp是一个String,其实就是一个UnicodeBSTR

    调用TestString时使用StrPtr(sp)将字符串的首地址传递给了DLLDLL的处理代码如下:

    {

    wchar_t*p = (wchar_t*)sp; //Unicode 字符串首地址

    if(p)

    {//VB6.0 传过来 vbNullString,则 sp 不为 NULLp NULL

    UINT&nLen2 = ((UINT*)p)[-1]; //Unicode 字符串字节数

    if(nLen2 >= 2)

    {

    p[0] = L's'; //允许修改字符串

    nLen2 = 2; //允许将字符串长度变小

    p[nLen2>>1] = L'\0'; //修改字符串长度后,这句是必需的

    }

    }

    }

    上述代码处理的不再是ANSI字符串,而是Unicode字符串。不过,修改字符串时只能减小长度,不能增大长度。

    3.3.4 ByVal vp As Long

    VB6.0里的变量vp是一个String,其实就是一个UnicodeBSTR

    调用TestString时使用VarPtr(vp)将字符串变量的地址传递给了DLLDLL的处理代码如下:

    {

    wchar_t*p = *(wchar_t**)vp; //Unicode 字符串首地址

    if(p)

    {//VB6.0 传过来 vbNullString,则 vp 不为 NULLp NULL

    UINT&nLen2 = ((UINT*)p)[-1]; //Unicode 字符串字节数

    if(nLen2 >= 2)

    {

    p[0] = L'v'; //允许修改字符串

    nLen2 = 2; //允许将字符串长度变小

    p[nLen2>>1] = L'\0'; //修改字符串长度后,这句是必需的

    }

    }

    }

    与"ByVal sp As Long"一样:修改字符串时只能减小长度,不能增大。

    3.3.5 返回值

    DLL函数返回一个ANSI编码的BSTR,如下所示:

    {//返回值,允许返回 NULL

    char*szStr    =    "C 函数返回的字符串";

    return SysAllocStringByteLen(szStr,strlen(szStr));

    }

    上述代码执行完毕后,VB6.0负责将其转换为Unicode编码的字符串。

    3.4 转换为Variant

    VB6.0里可以将字符串转换为Variant后传递给DLL。下面是示例代码:

    VC++代码:

    void WINAPI TestStringVariant(VARIANT v,VARIANT*r)

    {

    if(v.vt == VT_BSTR)

    {

    wchar_t*p = v.bstrVal; //Unicode 字符串首地址

    if(p)

    {

    UINT& nLen2 = ((UINT*)p)[-1]; //Unicode 字符串字节数

    if(nLen2 >= 2)

    {

    p[0] = L'';

    }

    }

    }

    if(r && r->vt == (VT_BSTR | VT_BYREF))

    {

    SysFreeString(*r->pbstrVal);

    *r->pbstrVal = SysAllocString(L"字符串2C 函数生成");

    }

    }

    VB6.0声明代码:

    Public Declare Sub TestStringVariant Lib "Test.dll" _

    (ByVal v As Variant, ByRef r As Variant)

    VB6.0调用代码:

    Dim s1 As String

    Dim s2 As String

    s1 = "字符串1"

    s2 = "字符串2"

    MsgBox Hex$(VarPtr(s1)) & "," & Hex$(StrPtr(s1)) & " = " & s1 & _

    vbCrLf & Hex$(VarPtr(s2)) & "," & Hex$(StrPtr(s2)) & " = " & s2

    Call TestStringVariant(s1, s2)

    MsgBox Hex$(VarPtr(s1)) & "," & Hex$(StrPtr(s1)) & " = " & s1 & _

    vbCrLf & Hex$(VarPtr(s2)) & "," & Hex$(StrPtr(s2)) & " = " & s2

    说明:

    1Call TestStringVariant(s1, s2)VB6.0会将s1s2转换为Variant,然后传递给DLL

    2DLL里的参数vv.bstrVal就是StrPtr(s1),修改v.bstrVal会影响s1。不过这种修改只能修改内容、减小长度,不能增大长度;

    3DLL里的参数rr->pbstrVal*r->pbstrVal分别是VarPtr(s2)StrPtr(s2)。允许为*r->pbstrVal分配一个新的Unicode字符串,Call TestStringVariant(s1, s2)返回后StrPtr(s2)仍然是*r->pbstrVal

    3.5 小结

    1、最常用的就是ByVal v As String

    如果DLL里的函数只是读取字符串,不修改字符串,使用比较简单。如下面的代码调用SetWindowText修改窗口标题栏文本:

    Private Declare Function SetWindowText Lib "user32" Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String) As Long

     

    Private Sub Form_Load()

    SetWindowText Me.hwnd, "窗口标题"

    End Sub

    如果DLL里的函数要修改字符串,就需要VB6.0分配内存,然后还要修改字符串长度。如下面的代码将获得Windows的安装目录:

    Private Declare Function GetWindowsDirectory Lib "kernel32" Alias "GetWindowsDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) As Long

     

    Private Sub Command1_Click()

    Dim s As String, n As Long

    s = Space$(260) '分配内存

    GetWindowsDirectory s, 260

    n = InStr(1, s, Chr$(0), vbBinaryCompare)

    If n >= 1 Then s = Left$(s, n - 1) ''\0'及其后面的字符删除

    End Sub

    2、若要处理ANSI字符串,并想加长,请使用ByRef r As String

    3、若要处理Unicode字符串,并想加长,请使用ByRef r As Variant

    4 结构

    4.1 定义

    VB6.0的结构定义与C的结构定义,请参考下表:

    VB6.0

    C 

    Public Type vb6Type

    vByte As Byte

    vBool As Boolean

    vInt As Integer

    vLng As Long

    vSng As Single

    vDbl As Double

    vStr As String

    End Type

    #pragma pack(push,4)

    struct vb6Type

    {

    BYTE        vByte;

    VARIANT_BOOL    vBool;

    short            vInt;

    long            vLng;

    float            vSng;

    double        vDbl;

    BSTR        vStr;

    };

    #pragma pack(pop)

    注意:为了与VB6.0的结构相对应,C里的结构必须按4字节对齐。

    4.2 示例代码

    VB6.0里将结构传递给DLL的示例代码如下:

    VC++代码:

    #pragma pack(push,4)

    struct vb6Type

    {

    BYTE                vByte;

    VARIANT_BOOL    vBool;

    short                vInt;

    long                vLng;

    float                vSng;

    double                vDbl;

    BSTR                vStr;

    };

    #pragma pack(pop)

     

    vb6Type WINAPI TestStruct(vb6Type*r,vb6Type*vp)

    {

    {

    r->vByte = 255;

    SysFreeString(r->vStr);

    const char*szStr = "VC 字符串(ANSI)";

    r->vStr = SysAllocStringByteLen(szStr,strlen(szStr));

    }

    {

    vp->vDbl = 55.0;

    SysFreeString(vp->vStr);

    vp->vStr = SysAllocString(L"VC 字符串(Unicode)");

    }

    {//返回值

    vb6Type ret;

    ret.vByte = 0;

    ret.vBool = VARIANT_TRUE;

    ret.vInt = 2;

    ret.vLng = 3;

    ret.vSng = 4.0f;

    ret.vDbl = 5.0;

    const char*szStr = "VC 字符串";

    ret.vStr = SysAllocStringByteLen(szStr,strlen(szStr));

    return ret;

    }

    } 

    VB6.0声明代码:

    Public Type vb6Type

    vByte As Byte

    vBool As Boolean

    vInt As Integer

    vLng As Long

    vSng As Single

    vDbl As Double

    vStr As String

    End Type

     

    Public Declare Function TestStruct Lib "Test.dll" _

    (ByRef r As vb6Type, ByVal vp As Long) As vb6Type

    VB6.0调用代码:

    Dim vt As vb6Type

    vt.vByte = 0

    vt.vBool = True

    vt.vInt = 2

    vt.vLng = 3

    vt.vSng = 4!

    vt.vDbl = 5#

    vt.vStr = "VB 字符串"

    Dim vr As vb6Type

    vr = TestStruct(vt, VarPtr(vt))

    下面是对上述代码的详细说明:

    4.2.1 ByVal vp As Long

    VB6.0的代码"vr = TestStruct(vt, VarPtr(vt))"中,VarPtr(vt) 将返回结构vt的首地址。这个地址传递给了 C 函数里的形参vp,对vp的处理代码如下:

    {

    vp->vDbl = 55.0;

    SysFreeString(vp->vStr);

    vp->vStr = SysAllocString(L"VC 字符串(Unicode)");

    }

    上述代码修改vDbl55.0;修改vStr为"VC 字符串(Unicode)"。注意:修改字符串前需要释放已有的字符串;vp->vStrUnicode编码的BSTR

    因为传递的是地址,所以形参的改变会引起VB6.0中实参的改变。

    4.2.2 ByRef r As vb6Type

    这个参数要分两种情况讨论:

    若结构里只有简单数据类型,没有字符串成员变量vStr,则直接传递结构变量的首地址,即VarPtr(vt)

    若结构里有字符串成员变量vStr,则VB6.0会生成一个临时结构变量。将实参数值复制给这个临时结构变量。数值复制时需要注意的是字符串,复制到临时变量里的字符串是ANSI编码的。调用完DLL里的函数后,VB6.0会将临时变量的数值再复制给实参。

    对形参r C 处理代码如下:

    {

    r->vByte = 255;

    SysFreeString(r->vStr);

    const char*szStr = "VC 字符串(ANSI)";

    r->vStr = SysAllocStringByteLen(szStr,strlen(szStr));

    }

    最重要的就是:r->vStr是一个ANSI编码的字符串。

    4.2.3 返回值

    DLL里的TestStruct函数返回了一个结构,这个结构里的字符串成员变量也是ANSI编码的。

    5 数组

    VB6.0里的数组对应于VC++里的SAFEARRAY。下面举例说明:

    5.1 简单数据类型数组

    数组的元素为简单数据类型时,示例代码如下:

    VC++代码,导出函数 TestArrayDouble

    void ReadArray(SAFEARRAY*sa)

    {

    if(NULL == sa

    || sa->cDims != 2 //二维数组

    || sa->rgsabound[0].lLbound != 3 //第一维下标从 3 开始

    || sa->rgsabound[0].cElements != 3 //第一维有 3 个元素

    || sa->rgsabound[1].lLbound != 0 //第二维下标从 0 开始

    || sa->rgsabound[1].cElements != 4 //第二维有 4 个元素

    )

    {//数组是二维的。

    //第一维下标从35(3),第二维下标从03(4)

    //注意:VB6.0 里数组 a 的第一、二维与 sa 的第一、二维正好逆序

    return;

    }

    {//读取单个元素 a(1,4),也就是读取 sa 数组中的元素(4,1)

    long n[2] = {1,4}; //(4,1)的逆序

    double d = 0.0;

    SafeArrayGetElement(sa,n,&d);

    }

    }

     

    void WINAPI TestArrayDouble(SAFEARRAY**r)

    {

    ReadArray(*r);

    }

    VB6.0声明代码:

    Public Declare Sub TestArrayDouble Lib "Test.dll" (ByRef r() As Double)

    VB6.0调用代码:

    Dim a(0 To 3, 3 To 5) As Double

    a(1, 4) = 10#

    TestArrayDouble a

    5.2 转换为Variant

    数组的元素为简单数据类型时,数组可以转换为Variant。请参考示例代码:

    VC++代码,导出函数TestArrayVariant

    void ReadArray(SAFEARRAY*sa)

    {

    if(NULL == sa

    || sa->cDims != 2 //二维数组

    || sa->rgsabound[0].lLbound != 3 //第一维下标从 3 开始

    || sa->rgsabound[0].cElements != 3 //第一维有 3 个元素

    || sa->rgsabound[1].lLbound != 0 //第二维下标从 0 开始

    || sa->rgsabound[1].cElements != 4 //第二维有 4 个元素

    )

    {//注意:VB6.0 里数组 a 的第一、二维与 sa 的第一、二维正好逆序

    return;

    }

    {//读取单个元素 a(1,4),也就是读取 sa 数组中的元素(4,1)

    long n[2] = {1,4}; //(4,1)的逆序

    double d = 0.0;

    SafeArrayGetElement(sa,n,&d);

    }

    }

     

    VARIANT WINAPI TestArrayVariant(VARIANT v,VARIANT*r)

    {

    {

    SAFEARRAY*sa = NULL;

    if(v.vt == (VT_ARRAY | VT_R8))

    {

    sa = v.parray;

    ReadArray(sa);

    }

    }

    {

    SAFEARRAY*sa = NULL;

    if(r->vt == (VT_ARRAY | VT_R8 | VT_BYREF))

    {//实参是一个变量,如:a

    sa = *r->pparray;

    }

    else if(r->vt == (VT_ARRAY | VT_R8))

    {//实参是一个临时变量,如:(a)

    sa = r->parray;

    }

    ReadArray(sa);

    }

    {//返回值

    VARIANT vRet;

    VariantInit(&vRet);

    VariantCopy(&vRet,&v);

    return vRet;

    }

    }

    VB6.0声明代码:

    Public Declare Function TestArrayVariant Lib "Test.dll" _

    (ByVal v As Variant, ByRef r As Variant) As Variant

    VB6.0调用代码:

    Dim a(0 To 3, 3 To 5) As Double

    a(1, 4) = 10#

    Dim vr As Variant

    vr = TestArrayVariant(a, a)

    5.3 结构数组

    VB6.0里将结构数组传递给DLL的示例代码如下:

    VC++代码,导出函数TestArrayStruct

    #pragma pack(push,4)

    struct vb6Type

    {

    BYTE                vByte;

    VARIANT_BOOL    vBool;

    short                vInt;

    long                vLng;

    float                vSng;

    double                vDbl;

    BSTR                vStr;

    };

    #pragma pack(pop)

     

    void WINAPI TestArrayStruct(SAFEARRAY**r)

    {

    if(r)

    {

    SAFEARRAY* psa = *r;

    if(psa)

    {

    vb6Type*pType = (vb6Type*)psa->pvData;

    if(pType

    && psa->cDims == 1 //一维数组

    && psa->cbElements == sizeof(vb6Type)) //单个元素字节

    {

    int n = (psa)->rgsabound->cElements; //元素个数

    const wchar_t*pStr = pType[0].vStr; //宽字符串

    SysFreeString(pType[1].vStr);

    pType[1].vStr = SysAllocString(L"VC 字符串");

    }

    }

    }

    }

    VB6.0声明代码:

    Public Type vb6Type

    vByte As Byte

    vBool As Boolean

    vInt As Integer

    vLng As Long

    vSng As Single

    vDbl As Double

    vStr As String

    End Type

     

    Public Declare Sub TestArrayStruct Lib "Test.dll" (ByRef r() As vb6Type)

    VB6.0调用代码:

    Dim a(0 To 3) As vb6Type

    a(0).vStr = "VB 字符串"

    Call TestArrayStruct(a)

    执行完毕后a(1).vStr变为"VC 字符串"

    6 Object

    VB6.0里将Object传递给DLL的示例代码如下:

    VC++代码:

    void WINAPI TestObject(IDispatch*v,IDispatch**r,IDispatch**vp)

    {

    CComDispatchDriver sp(v);

    sp.PutPropertyByName(L"Caption",&_variant_t(L"VC 设置"));

    }

    VB6.0声明代码:

    Public Declare Function TestObject Lib "Test.dll" (ByVal v As Object, _

    ByRef r As Object, ByVal vp As Long) As Variant

    VB6.0调用代码:

    Private Sub cmdObject_Click()

    Call TestObject(cmdObject, cmdObject, VarPtr(cmdObject))

    End Sub

    执行完毕后按钮cmdObject内的文本将变为"VC设置"。

    7 函数

    VB6.0里将函数传递给DLL的示例代码如下:

    VC++代码:

    void WINAPI TestFunc(void (WINAPI*vb6Func)(BSTR sv,BSTR*sr,int nv,int*nr))

    {

    if(vb6Func)

    {

    BSTR    sv    =    SysAllocString(L"参数 sv");

    BSTR    sr    =    SysAllocString(L"参数 sr");

    int        nv    =    1;

    int        nr    =    2;

    (*vb6Func)(sv,&sr,nv,&nr);//调用 VB6.0 函数,sr nr 的值可变

    SysFreeString(sv);

    SysFreeString(sr);

    }

    }

    VB6.0声明代码:

    Public Declare Sub TestFunc Lib "Test.dll" (ByVal v As Long)

    VB6.0调用代码(请放在 Module 模块里)

    Private Sub vb6Func(ByVal sv As String, ByRef sr As String, _

    ByVal nv As Long, ByRef nr As Long)

    MsgBox sv & "," & sr & "," & nv & "," & nr

    sv = "A"

    sr = "B"

    nv = 10

    nr = 20

    End Sub

     

    Public Function vb6TestFunc()

    Call TestFunc(AddressOf vb6Func)

    End Function

    执行VB6.0函数vb6TestFunc,会调用C函数TestFunc,其参数为VB6.0函数vb6Func的地址。

    展开全文
  • JAVA VB C++ DLL接口调用实例Examples.rar
  • pb调用dll实例

    热门讨论 2013-03-12 14:14:15
    (5)DLL独立于编程语言,大多数WINDOWS编程环境都允许主程序调用DLL中的函数。即可以用VC++、VB、PowerBuilder、Delphi、汇编语言等建立DLL,然后在不同语言编制的应用程序中调用它。下面用一个实例说明通过DLL实现...
  • VB如何调用DLL

    千次阅读 2014-04-21 16:09:22
    Windows动态连接库是包含数据和函数的模块,可以被其它可执行文件(EXE、DLL、OCX 等)调用。动态连接库包含两种函数:输出(exported)函数和内部(internal)函数。输出函数可以被其它模块调用,而内部函数则只能...
    Windows动态连接库是包含数据和函数的模块,可以被其它可执行文件(
    EXEDLLOCX 等)调用。动态连接库包含两种函数:输出(
    exported)函数和内部(
    internal)函数。输出函数可以被其它模块调用,而内部函数则只能在动态连接库内部使用。尽管动态连接库也能输出数据,但实际上它的数据通常是只在内部使用的。使用动态连接库的优点是显而易见的。将应用程序的一部分功能提取出来做成动态连接库,不但减小了主应用程序的大小,提高了程序运行效率,还使它更加易于升级。多个应用程序共享一个动态连接库还能有效地节省系统资源。正因为如此,在
    Windows系统中,动态连接库得到了大量的使用。 
    

      一般来说,动态连接库都是以DLL为扩展名的文件,如Kernel32.dllcommdlg.dll等。但也有例外,如16Windows的核心部件之一GDI.exe其实也是一个动态库。编写动态连接库的工具很多,如VisualC++BorlandC++Delphi等,具体方法可以参见相关文档。下面只以Visual C++6.0为例,介绍一下开发应用于VisualBasic6.0的动态连接库时应注意的问题(本文中所有涉及C/C++语言或编译环境的地方,都以VC为例;所有涉及Visual Basic的地方都以VB 为例)。

      作为一种32Windows应用程序的开发工具,VB生成的exe文件自然也都是32位的,通常情况下也只能调用32位的动态连接库。但是,并不是所有的32位动态库都能被VB生成的exe 文件正确地识别。一般来说,自己编写用于VB应用程序调用的动态连接库时,应注意以下几个方面的

    问题:

      1、生成动态库时要使用__stdcall调用约定,而不能使用缺省的__cdecl调用约定;__stdcall 约定通常用于32API函数的调用。

      2、在VC中的定义文件(.def)中,必须列出输出函数的函数名,以强制VC系统将输出函数的装饰名(decoratedname)改成普通函数名;所谓装饰名是VC的编译器在编译过程中生成的输出函数名,它包含了用户定义的函数名、函数参数及函数所在的类等多方面的信息。由于在VC中定义文件不是必需的,因此工程不包含定义文件时VC就按自己的约定将用户定义的输出函数名修改成装饰名后放到输出函数列表中,这样的输出函数在VB生成的应用程序中是不能正确调用的(除非声明时使用Alias子句)。因此需要增加一个.def文件,其中列出用户需要的函数名,以强制VC不按装饰名进行输出。

      3VC中的编译选项"结构成员对齐方式(structure member alignment" 应设成4字节,其原因将在后文详细介绍。

      4、由于在C中整型变量是4个字节,而VB中的整型变量依然只有2个字节,因此在C中声明的整型(int)变量在VB中调用时要声明为长整型(long),而C中的短整型(short)在VB中则要声明成整型(integer);下表针对最常用的C语言数据类型列出了与之等价的Visual Basic 类型(用于32位版本的Windows)。

    C语言数据类型在VisualBasic中声明为调用时使用的表达式

       ATOM ByVal variable As Integer 结果为Integer 类型的表达式

       BOOL ByVal variable As Long 结果为 Long 类型的表达式

       BYTE ByVal variable As Byte 结果为 Byte 类型的表达式

       CHAR ByVal variable As Byte 结果为 Byte 类型的表达式

       COLORREF ByVal variable As Long 结果为 Long 类型的表达式

       DWORD ByVal variable As Long 结果为 Long 类型的表达式

       HWND, HDC, HMENU ByVal variable As Long 结果为 Long 类型的表达式等Windows 句柄

       INT, UINT ByVal variable As Long 结果为 Long 类型的表达式

       LONG ByVal variable As Long 结果为 Long 类型的表达式

       LPARAM ByVal variable As Long 结果为 Long 类型的表达式

       LPDWORD variable As Long 结果为 Long 类型的表达式

       LPINT, LPUINT variable As Long 结果为 Long 类型的表达式

       LPRECT variable As type 自定义类型的任意变量

       LPSTR, LPCSTR ByVal variable As String 结果为 String 类型的表达式

       LPVOID variable As Any 任何变量(在传递字符串的时候使用ByVal

       LPWORD variable As Integer 结果为Integer 类型的表达式

       LRESULT ByVal variable As Long 结果为 Long 类型的表达式

       NULL As Any ByVal Nothing

       ByVal variable As Long ByVal 0& VBNullString

       SHORT ByVal variable As Integer 结果为Integer 类型的表达式

       VOID Sub procedure 不可用

       WORD ByVal variable As Integer 结果为Integer 类型的表达式

       WPARAM ByVal variable As Long 结果为 Long 类型的表达式

      5VB中进行32位动态库的声明时,函数名是大小写敏感的。在获得了需要的动态连接库之后,就可以在VB中进行调用了。但是,由于VB不能验证应用程序传递到动态连接库中的参数值是否正确,因此VB程序中大量的API调用可能会降低整个应用程序的稳定性,也会增加以后维护的难度。所以,决定在VB程序中直接调用API函数时要慎重,但适当的使用API调用确实能够有效地提高VB程序的性能。这之间的平衡需要编程人员根据实际情况来掌握。下面就具体介绍一下在VB中调用API函数时需要做的工作。

      要声明一个DLL过程,首先需要在代码窗口的"通用(General"部分增加一个Declare语句。如果该过程返回一个值,应将其声明为

    Function

       Declare Function publicname Lib "libname" [Alias "alias"] [([[ByVal] variable [As type] [,[ByVal] variable [As type]]...])] As Type 

      如果过程没有返回值,可将其声明为Sub

       Declare Sub publicname Lib "libname" [Alias "alias"] [([[ByVal] variable [As type] [,[ByVal] variable [As type]]...])] 

      缺省情况下,在标准模块中声明的DLL过程,可以在应用程序的任何地方调用它。在其它类型的模块中定义的DLL过程则是模块私有的,必须在它们前面声明Private关键字,以示区分。下面分别介绍声明语句的各个组成部分。

      ()、指定动态库:

      Declare语句中的Lib子句用来告诉Visual Basic如何找到包含过程的.dll文件。如果引用的过程属于Windows核心库(User32Kernel32GDI32),则可以不包含文件扩展名,如:

       Declare Function GetTickCount Lib "kernel32" Alias "GetTickCount" () As Long 

      对于其它动态连接库,可以在Lib子句指定文件的路径:

       Declare Function lzCopy Lib "c:/windows/lzexpand.dll" _

       (ByVal S As Integer, ByVal D As Integer) As Long 

    如果未指定libname的路径,Visual Basic将按照下列顺序查找该文件:

      ①.exe文件所在的目录

      ②当前目录

      ③Windows系统目录

      ④Windows目录

      ⑤Path环境变量中的目录

      下表中列出了常用的操作系统环境库文件。

      动态链接库描述

      Advapi32.dll高级API服务,支持大量的API(其中包括许多安全与注册方面的调用)

      Comdlg32.dll通用对话框API

      Gdi32.dll图形设备接口API

      Kernel32.dllWindows32位核心的API支持

      Lz32.dll32位压缩例程

      Mpr.dll多接口路由器库

      Netapi32.dll32位网络API

      Shell32.dll32ShellAPI

      User32.dll用户接口例程库

      Version.dll版本库

      Winmm.dllWindows多媒体库

      Winspool.drv后台打印接口,包含后台打印API调用。

      对于Windows的系统API函数,可以利用VB提供的工具API Viewer查找某一函数及其相关数据结构和常数的声明,并复制到自己的程序中。

    ()、使用别名:

    A.函数名是标准的名称

      Declare语句中的Alias子句是一个可选的部分,用户可以通过它所标识的别名对动态库中的函数进行引用。例如,在下面的语句中,声明

    了一个在VB中名为MyFunction的函数,而它在动态库Mydll.dll中最初的名字是MyFunctionX

       Private Declare Function MyFunction Lib "Mydll.dll" _

       Alias "MyFunctionX" ( ) As Long 

      需要注意的是,Alias子句中的函数名是大小写敏感的,也就是说,必须与函数在生成时的声明(如在C源文件中的声明)一致。这是因为32位动态库与16位动态库不同,其中的函数名是区分大小写的。同样道理,如果没有使用Alias子句,那么在Function(或Sub)后的函数名也是区分大小写的。

      通常在以下几种情况时需要使用Alias子句:

    A.处理使用字符串的系统Windows API过程

      如果调用的系统Windows API过程要使用字符串,那么声明语句中必须增加一个Alias 子句,以指定正确的字符集。包含字符串的系统Windows API函数实际有两种格式:ANSIUnicode(关于ANSIUnicode两种字符集的区别将在后面详细阐述)。因此,在Windows头文件中,每个包含字符串的函数都同时有ANSI版本和Unicode版本。例如,下面是SetWindowText函数的两种C语言描述。可以看到,第一个描述将函数定义为SetWindowTextA,尾部的"A" 表明它是一个ANSI函数:

       WINUSERAPI BOOL WINAPI SetWindowTextA(HWND hWnd, LPCSTR lpString); 

      第二个描述将它定义为 SetWindowTextW,尾部的"W" 表明它是一个 Unicode 函数:

       WINUSERAPI BOOL WINAPI SetWindowTextW(HWND hWnd, LPCWSTR lpString); 

      因为两个函数实际的名称都不是"SetWindowText",要引用正确的函数就必须增加一个Alias子句:

    Private Declare Function SetWindowText Lib "user32" _

    Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal _

    lpString As String) As Long

      应当注意,对于VB中使用的系统WindowsAPI函数,应该指定函数的ANSI版本,因为只有WindowsNT才支持Unicode版本,而Windows95不支持这个版本。仅当应用程序只运行在WindowsNT平台上的时候才可以使用Unicode版本。

      B.函数名是不标准的名称

      有时,个别的DLL过程的名称不是有效的标识符。例如,它可能包含了非法的字符(如连字符),或者名称是VB的关键字(如GetObject)。在这种情况下,可以使用Alias关键字。例如,操作环境DLLs中的某些过程名以下划线开始。尽管在VB标识符中允许使用标识符,但是下划线不能作为标识符的第一个字符。为了使用这种过程,必须先声明一个名称合法的过程,然后用Alias子句引用过程的真实名称:

    Declare Function lopen Lib "kernel32" Alias "_lopen" _

    (ByVal lpPathName As String, ByVal iReadWrite _

    As Long) As Long

      在上例中,lopenVB中使用的过程名称。而_lopen则是动态连接库中可以识别的名称。

      C.使用序号标识DLL过程

      除了使用名称之外,还可以使用序号来标识DLL过程。某些动态连接库中不包含过程的名称,在声明它们包含的过程时必须使用序号。同使用名称标识的DLL过程相比,如果使用序号,在最终的应用程序中消耗的内存将比较少,而且速度会快些。但是,一个具体的API的序号在不同的操作系统中可能是不同的。例如GetWindowsDirectoryWin95下的序号为432,而在WindowsNT4.0下为338。总而言之,如果希望应用程序能够在不同的操作系统下运行,那么最好不要使用序号来标识API过程。如果过程不属于API,或者应用程序使用的范围很有限,那么使用序号还是有好处的。

      要使用序号来声明DLL过程,Alias子句中的字符串需要包含过程的序号,并在序号的前面加一个数字标记字符(#)。例如,Windowskernel中的GetWindowsDirectory函数的序号为432;可以用下面的语句来声明该DLL过程:

    Declare Function GetWindowsDirectory Lib "kernel32" _

    Alias "#432" (ByVal lpBuffer As String, _

    ByVal nSize As Long) As Long

      在这里,可以使用任意的合法名称作为过程的名称,VB将用序号在DLL中寻找过程。

      为了得到要声明的过程的序号,可以使用Dumpbin.exe等实用工具(Dumpbin.exeMicrosoft VisualC++提供的一个实用工具,它的使用说明可以参见VC的文档)。利用Dumpbin,可以提取出.dll文件中的各种信息,例如DLL中的函数列表,它们的序号以及与代码有关的其它信息。

      ()、使用值或引用传递

      在缺省的情况下,VB以引用方式传递所有参数(ByRef)。这意味着并没有传递实际的参数值,VB只传递了数据的32位地址。另外有许多DLL过程要求参数以值方式传递(ByVal)。这意味着它们需要实际的数据,而不是数据的内存地址。如果过程需要一个传值参数,而传递给它的参数是一个指针,那么由于得到了错误的数据,该过程将不能正确地工作。要使参数以使用值方式传递,在Declare语句中需要在参数声明的前面加上ByVal关键字。例如InvertRect过程要求第一个参数用传值方式传递,而第二个用引用方式传递:

    Declare Function InvertRect Lib "user32" Alias _

    "InvertRectA" (ByVal hdc As Long, lpRect As RECT) As Long

      动态连接库的参数传递是一个复杂的问题,也是VB中调用动态连接库时最容易出现错误的地方。参数类型或传递方式的声明错误都可能导致应用程序出现GPF(通用保护错误),甚至使操作系统崩溃,因此我们将在后面专门详细地讨论这个问题。

      ()、灵活的参数类型

      某些DLL过程的同一个参数能够接受多种数据类型。如果需要传递多种类型的数据,可以将参数声明为AsAny,从而取消类型限制。例如,下面的声明中的第三个参数(lpptAsAny) 既可以传递一个POINT结构的数组,也可以传递一个RECT结构:

    Declare Function MapWindowPoints Lib "user32" Alias _

    "MapWindowPoints" (ByVal hwndFrom As Long, _

    ByVal hwndTo As Long, lppt As Any, _

    ByVal cPoints As Long) As Long

      As Any子句提供了一定的灵活性,但是,由于它不进行任何的类型检查,风险也随之增加。因此在使用AsAny子句时,必须仔细检查所有参数的类型。正确的函数声明是在VB中调用动态连接库的前提,但要想在VB中用对、用好动态库中的函数,仅仅有声明还是远远不够的。前面已经说过,由于VB不能验证应用程序传递到动态连接库中的参数值是否正确,因此就要求程序员应对参数类型有非常详细的了解,否则很容易引起应用程序发生通用保护错或导致潜在的Bug,降低软件的可靠性。下面将参数类型分为简单数据类型、字符串、和用户自定义类型三种分别进行讨论。

      (1)、简单数据类型:

      简单数据类型是指Numeric数据类型(包括IntegerLongSingleDoubleCurrency类型)、Byte数据类型和Boolean数据类型。它们的共同的特点是结构简单,操作系统在处理时不必进行特殊的转换。

      简单数据类型参数的传递比较简单。我们知道,在VB中传递参数的方式有两种:传值(Byval)和传址(ByRef),缺省的方式是传址。所谓传值,就是对一个变量的具体值进行传递;而传址则是传递变量的地址。例如,在VB程序中需要将一个整型变量m=10的值传进动态库,如果用传值方式,那么传进动态库的值就是10,而在传址方式下,传入的则是变量m的地址,相当于C/C++ &m的值。需要注意的是,以传值方式传进动态连接库的变量,其值在动态库中是不能被改变的;如果需要在动态连接库中修改传入参数的值,则必须使用传址方式。一般来说,在VB 和动态连接库之间传递单个的简单数据类型,只要注意了以上几个方面就可以了。当需要将一个简单数据类型的整个数组传进动态库时,必须将相应参数声明为传址方式,然后把数组的第一个元素作为参数传入,这样在动态连接库中就得到了数组的首地址,从而可以对整个数组进行访问。例如,声明了一个名为ReadArrayDLL过程,要求传入一个整型数组aArray

    Declare Function ReadArray Lib "mydll.dll" _

    (aArray As Integer) As Integer

    在调用时可以采用如下方式:

    Dim ret,I(5) as Integer

    ret = ReadArray(I(0)) '

    将整个数组传入动态连接库

    (2)、字符串参数的传递:

      与简单数据类型相比,字符串类型(StringString*n)的参数传递要复杂得多,这主要是Windows 95 APIVB使用的字符串类型不同的缘故。VB使用被称为BSTRString数据类型,它是由自动化(以前被称为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结尾的字符串数据的第一个字节

      如果DLL过程需要一个LPSTR(指向以null结束的字符串的指针)作为参数,可以在VB 中将一个字符串以传值的方式传递给它。因为指向BSTR的指针实际指向以null值结束的字符串的第一个数据字节,所以对于DLL过程来说,它就是一个LPSTR。这样传入动态连接库的字符串,DLL过程也可以对它进行修改,尽管它是以传值方式传入的。只有当DLL过程需要一个指向LPSTR的指针时,才以传址的方式传入字符串,这时DLL过程得到的是一个指向字符串指针的指针(相当于C/C++中的char**),而不是通常所用的字符串的首地址(相当于C/C++中的char*)。

      当需要把一个字符串数组整个传入动态连接库时,情况就变得复杂多了,用传递简单数据类型数组的方式来传递字符串数组是行不通的。当我们以传值的方式将一个字符串数组的第一个元素传进动态连接库时,DLL过程得到的实际上是该元素压入堆栈段后的地址,而不是数据段中整个数组的首地址。也就是说,这时DLL过程只能得到数组的第一个元素,而无法访问整个数组。而以传址方式传入第一个元素时,DLL过程只能得到指向该元素在堆栈段中地址的指针,同样无法访问整个数组。这不能不说是VB的一个不足。因此,在程序设计中,如果确实需要将整个字符串数组传入动态库,就必须采取其它方法。

      我们知道,在VB中,有一种Byte数据类型。每个Byte型变量占一个字节,不含符号位,因此所能表示的范围为0255。这种数据类型是专门用于存放二进制数据的。为了将整个字符串数组传进动态库,可以用字节数组来保存字符串。由于Byte是一种简单数据类型,因此字节数组的传递是非常简单的。首先,需要把一个字符串正确地转变成一个字节数组。这要涉及一些字符集的知识。Windows 95VB使用不同的字符集,Windows 95 API使用的是ANSIDBCS 字符集,而VB使用的则是Unicode字符集。所谓ANSI字符集,是指每个字符都用一个字节表示,因此最多只能有28=256个不同的字符,这对于英语来说已经足够了,但不能完全支持其它语言。DBCS字符集支持很多不同的东亚语言,如汉语、日语和朝鲜语,它使用数字0-255表示ASCII 字符,其它大于255或小于0的数字表明该字符属于非拉丁字符集;在DBCS中,ASCII字符的长度是一个字节,而汉语、日语和其它东亚字符的长度是2个字节。而Unicode字符集则完全用两个字节表示一个字符,因此最多可以表示216=65536个不同字符。也就是说,ANSI字符集中所有的字符都只占一个字节,DBCS字符集中ASCII字符占一个字节,汉字占两个字节,Unicode 字符集中每个字符都占两个字节。由于VBWindowsAPI使用的字符集不同,因此在进行字符串到字节数组的转换时,当用Asc函数取得一个字符的字节码后,需要判断它是否是一个ASCII 字符;如果是ASCII字符,则在转换后的字节数组中就只占一个字节,否则要占两个字节。

      下面给出了转换函数:GetChar Byte得到一个字符的高字节或低字节,它的第一个参数是一个字符的ASCII码,第二个参数是标志取高字节还是低字节;StrToByteDBCSANSI格式将一个字符串转换成一个字节数组,第一个参数是待转换的字符串,第二个参数是转换后的一个定长字节数组,若该数组长度不足以存放整个字符串,则截去超长的部分;ChangeStrAryToByte 利用前两个函数将字符串数组转换成字节数组,第一个参数是定长的字符串数组,其中每个元素都是一个字符串(各个元素包含的字符数可以不同),第二个参数是一个变长的字节数组,保存转换后的结果。

      当转换完成以后,查看字节数组ResultAry,其中包含了21个元素,依次是:178226202212490178226202212505050017822620221251510。其中,[178226]""的字节码,[202112]""的字节码,495051 分别为字符123ASCII码。可见,经过转换后,字符串数组中的各个元素按顺序放在了字节数组中,相互间以终止符0分隔。

      这样,字符串数组就全部转换成了字节数组,然后只要将字节数组的第一个元素以传址的方式传入动态连接库,DLL过程就可以正确地访问数组中的所有字符串了。但是,使用这种方法,当DLL过程处理结束返回VB时,VB得到的仍然是字节数组。如果需要在VB中再次得到该字节数组表示的字符串,还要把整个字节数组重新以0为分割符分成多个子数组(每个子数组都对应原来字符串数组中的一个元素),然后使用VB函数StrConv将每个子数组转换成字符串(转换时第二个参数选vbUnicode),就可以显示或进行其它操作了。例如,其中一个子数组的名字是SubAry,则函数StrConv(SubAry,vbUnicode)就返回了它所对应的字符串。

      总之,VB应用程序和动态库间字符串参数的传递是一个比较复杂的过程,使用时要非常谨慎。同时应尽可能避免传递字符串数组类型的参数,因为这很容易引起下标越界、堆栈溢出等严重错误。

    (3)、用户自定义类型(User-defined Type)参数的传递

      用户自定义类型在VB中是一种重要的数据类型,它为编程者提供了很大的灵活性,使开发人员可以根据需要构造自己的数据结构。它相当于C/C++中的结构类型(structure)。在VB中,允许程序员以传址的方式将自定义数据类型参数传入动态库,DLL过程也可以将修改后的参数返回VB程序。但是,在VB中仍然不支持以传值的方式传递用户自定义类型参数。

      传递用户自定义类型参数时,必须确保VB中的数据类型的成员与动态库中的结构成员是一一对应的,所占空间也必须严格一致。这里所说的一一对应,不仅是指VB 中的所有结构成员在动态库的结构中都必须有对应的元素,而且它们在数据结构中定义的顺序也必须严格一致,这是VB中使用的"数据结构成员对齐方式"决定的。在VB 中,数据结构使用双字对齐方式(4-byte alignment),因此,在用户自己生成用于VB 调用的动态连接库时,也必须把编译选项"structure member alignment" 设为4字节(如前文所述)。

      所谓结构成员对齐方式是指一个数据结构内部,其成员的排列方式。譬如,在VB中,其对齐方式是4字节,这就好象在一个数据结构内部分成了很多个4字节大小的小单元,如果相邻两个或多个数据成员的大小可以放在一个单元中,那么就放在一起;否则这些小单元中可能会出现未用的空字节。我们来看下面一个数据类型:

    Type TestType

    m1 as Integer

    m2 as Byte

    m3 as Long

    End Type

      它的三个成员的大小加起来是2+1+4=7。但是,由于m1m2的字节总长度是3,小于4,它们就存放于一个单元中;但该单元剩下的一个字节不足以放下一个Long型的成员m3,于是m3 就被放在下一个单元中,它们之间就有了一个未用的空字节;因此,整个结构所占实际长度是8 字节。同理,如果将m3m2的位置交换一下,它所占的尺寸就变成了9字节。可见,成员在结构中的声明顺序也是非常重要的。

      通常,当一个用户自定义类型中不包含字符串时,向动态连接库中传递该类型的参数是没有什么问题的。如果只传递一个自定义类型变量,则既可以传递该变量名,也可以传递该变量的第一个成员,它们的效果是一样的,都是将该变量的地址传进了动态库;同样,如果要传递一个自定义类型的数组,则既可以传递该数组的第一个元素,也可以传递第一个元素的第一个成员。但是,如果用户自定义类型中包含字符串类型时,又该如何与动态连接库传递参数呢?答案是令人遗憾的:在VB中,你无法将一个包含字符串成员的用户自定义类型变量或数组安全、正确地传入动态库中。如果你这样做了,即使某次侥幸得到了正确的结果,在其背后也隐藏着许多致命的危险。因此,如果一定要在用户自定义类型中包含字符串变量,并且该类型的变量又要作为参数传入动态库时,你最好修改类型定义,把其中的字符串成员用相应的字节数组类型替换掉(转换方法可参见前文),这样就可以在VB 和动态库间传递这种类型的参数了。

      另外,在VB 中还可以把一个函数的指针传递到动态库中,方法也并不复杂。但笔者强烈建议最好不要这么做,因为这样一来VB 应用程序就几乎完全丧失了它所应有的安全性。如果确实需要传递函数指针的话,那么还是编一个C/C++ 的程序来完成这项工作吧。

    总之,在VB中调用DLL过程是一个比较复杂的问题,编程人员必须很好地把握,才能达到既提高了程序效率,开拓了程序功能,又不降低程序安全性的目的。另外需要特别指出的一点是,在本文中提到的所有动态连接库,都是指没有使用自动化(OLE Automation)技术的动态库,Windows API和大多数用户自编的动态连接库都是这种类型的。对于使用了OLE Automation技术的动态连接库,其参数传递的方式有所不同,读者可以参阅有关OLE 技术的书籍,在此不再涉及。

    展开全文
  • VB封装DLL实例(三)

    千次阅读 2017-02-11 17:40:46
    进入VBA编辑窗口,点菜单【工具】—【引用】,打开【引用】对话框,点【浏览】按钮,打开【添加引用】对话框,点选要引用的DLL(测试实例为:ClsFindString.dll),点【打开】—点【确定】,我们完成动态链接库的...
    正 文:
    

    一、手动注册及引用

    (一)手动注册及引用方法(参看实例:手动引用.mdb

    进入VBA编辑窗口,点菜单【工具】—【引用】,打开【引用】对话框,点【浏览】按钮,打开【添加引用】对话框,点选要引用的DLL(测试实例为:ClsFindString.dll),点【打开】—点【确定】,我们完成动态链接库的手动注册及引用。


    (二)手动注册及引用方法不足及问题

    手动注册引用优点是不言而喻的,方便简捷,易于操作。但在实际运用中,当我们在其他电脑上发布应用程序,或运行我们测试好的应用程序时,却会出现错误提示,程序无法正常运行。

    错误(一)找不到工程或库(见下图)

    错误的主要原因:DLL在当前运行的电脑系统中没有注册信息,而且引用不正确。




    错误(二)引用的动态链接库(DLL)丢失(见下图)

    进入到VBA编辑窗口,点菜单【工具】—【引用】,打开【引用】对话框,我们会看到之前引用的DLL动态链接库丢失。

    错误的主要原因:系统无法找到原路径引用DLL




    错误(三)自动化错误(见下图)

    错误的主要原因:我们在发布应用程序的电脑或系统中,虽然重新完成DLL手动注册和引用,但如果DLL路径再次改变,运行程序时就会出现“自动化错误”提示。


     

    错误(四):ActiveX部件不能创建对象(见下图)

    错误的主要原因:应用程序已正常引用DLL动态链接库,但其册注信息丢失或者没有正常注册,就会出现以下问题。


    (三)解决上述错误方法

    1、解决错误方法,当然是重新进行DLL的手动注册及引用,具体步骤参下图。但这只是治标不治本的办法,不利于对外发布我们的应用程序,最好的办法还是通过VBA自动完成DLL的注册及引用。



    二、自动注册及引用方法

    在探讨如何实现DLL自动注册及引用之前,我们必须清楚一点,那就是DLL的注册与引用并不是同一事件或行为的两种不同表述,而是两种不同的动作。

     

    ²        DLL注册

    是指将DLL的相关信息,如:DLL唯一识标号(GUID),版本号(Version)及路径(Path)信息写入注册表中,以供系统对DLL进行识别调用。

    我们通过VB编译生成DLL时,VB一般会自动完成对该DLL的注册,但如果要在其它电脑上运行程序时,我们就必须重新对该DLL进行注册。

     

    ²        DLL引用

    是指将DLL类库对象集成到代码编辑环境中,以便编程时调用类库中的对象、属性及方法。

    我们通过手动方式完成DLL的引用时,系统会自动完成对该DLL的注册,所以我们无需另行对DLL进行注册,但如果我们在其它电脑上运行程序时,还会出现我们在之前章节中所述的错误。

     

    (一)DLL自动注册方法

    我们可以通过 Regsvr32.exe 来进行DLL注册或反注册,具体的语法及参数:

    语法:

    Regsvr32 [/u] [/n] [/i[:cmdline]] dllname

    说明:其中dllname为DLL文件名,建议在发布时将DLL复制到system文件夹下。

    参数:

    参数

    说明

    /u

    反注册

    /s

    指定regsvr32安静运行,且不显示任何消息框。

    /n

    指定不调用DllRegisterServer。此选项必须与/i共同使用。

    /i:cmdline

    调用DllInstall 将它传递到可选的 [cmdline]。在与/u共同使用时,它调用dll卸载。

    dllname

    指定要注册的dll文件名。

     

    1.1 示例通过Shell运行Regsvr32程序完成DLL注册        

    Shell "Regsvr32 /S " & Chr(34) & CurrentProject.Path & "\ClsFindString.dll" & Chr(34)

    Shell 函数                  用以运行Regsver32程序

    Regsver32                      注册程序

    /S                                    注册程序参数,书写时记得参数前后必须留空

    Chr(34)                          Chr函数,获指定代码字符,Chr(34)为引号

    CurrentProject.Path    DLL当前路径

    ClsFindString.dll       演示实例DLL

     

    1.2 示例通过Shell运行Regsvr32程序反注册 

    Shell "Regsvr32 /U /S " & Chr(34) & CurrentProject.Path & "\ClsFindString.dll" & Chr(34)

     

    我们可以将注册语句放在窗体的加载事件,自动完成DLL的注册,具体可以参看实例。但如果我们有多个DLL需要批量注册时,可以考虑通过软件打包发布工具来完成DLL的注册工作;也可以事先编写BAT文件,在打包发布时将该BAT文件一并打包发布,安装时运行该BAT文件,来完成NDLL的批量注册,在此就不多着笔墨,大家可以参看实例包中的BAT文件实例。

     

    (二)DLL自动引用方法

    2.1 通过References对象的AddFromFile方法实现自动引用

     

    Dim ref As Reference   '申明引用类对象

    On Error Resume Next  '避免因重复引用造成的错误提示

    '实例化引用对象,完成DLL的引用

    Set ref = References.AddFromFile(CurrentProject.Path & "\ClsFindString.dll")

     

     

    为了避免因重复引用出现的错误,我们可以如上代码中加入Error语句,我们还可以在应用程序退出时,通过对References 对象的Remove 方法释放DLL或反引用。

     

    Dim ref As Reference   '申明引用类对象

    '实例化反引用对象

    Set ref = References("ClsFindString")

    '移除引用指定类库

    References.Remove ref

     

    说明:根据本人实践,我个人倾向于使用Error语句,因为如果应用程序非正常退出,引用对象没有反引用成功,启动时就难免出现重复引用的错误问题。

     

    2.2 通过DLL唯一标识号实现自动引用

     

    Dim ref As Reference   '申明引用类对象

    On Error Resume Next  '避免因重复引用造成的错误提示

    '唯一标识号完成注册,需要DLL标识号,主版本号,次版本

    Set ref = References.AddFromGuid("{C5E340E2-C557-4852-AE83-5A0578B6863B}", 1, 0)

     

     

    DLL的标识号是编译生成时就确定了的,这个标识号就是DLL的终生制身份证号,我们可以通过这个唯一标识号来完成DLL自动引用。但此种方法必须具备两个条件,一是DLL已经成功注册,二是我们知道了该DLL的标识号、主版本号、次版本号。

     

    2.2.1获取DLL标识号、主版本号、次版本号方法

     

    Dim ref As Reference   '申明引用类对象

    '实例化引用类库对象

    Set ref = References.AddFromFile(CurrentProject.Path & "\ClsFindString.dll")

    Debug.Print ref.GUID    '获得DLL唯一标识号

    Debug.Print ref.Major    '获得主版本号

    Debug.Print ref.Minor    '获得次版本号

     

     

    2.3通过CreateObject方法实现自动引用

     

    Dim DllFindStr As Object  '申明对象

    '实例化对象为创建的DLL类库对象

    'ClsFindstringDLL库名,clsFindStrDLL类名

    Set DllFindStr = CreateObject("ClsFindstring.clsFindStr")

           DllFindStr.sFindStr Text2, Me.Text0  '调用DLL类库方法,运行程序

    Set DllFindStr = Nothing              '释放对象

     

     

    根据我本人实践经验,CreateObject方法自动引用是最为便捷高效的方法,仅供参考。

     

    关于DLL相关自动注册及引用方法就探讨这里,以上文字仅是本人实践的一点总结,希望对大家有所帮助,如有错漏之处还请大家斧正。本文的Word文稿、DLL源码(含DLL)、mdb演示实例及BAT注册文件实例均在实例包中。

    展开全文
  • DLL(VC)编写和VB调用

    2012-10-14 22:23:49
    DLL(VC)编写和VB调用,基础的调用实例
  • VB 调用TTS语音控件实例,工程开始前,请先引用TTSDLL.dll。实现电脑播音功能,程序运行时将获取捷通华声语音库,若库连接不上,可能会出现Bug,仅供参考学习。
  • 打开VB6,新建ActiveX DLL 2、在工程引用中加入Microsoft Active Server Pages Object Library选择 3、填加代码如下: 代码如下: ‘Code Start ‘声明部分 Private MyScriptingContext As ScriptingContext Private ...
  • VB封装DLL实例讲解,好东西呀!DLL即动态链接库(Dynamic Link Library),是由可被其它程序调用的函数集合组成的可执行文件模块。DLL不是应用程序的组成部分,而是运行时链接到应用程序中。用VB封装VBA代码,可以...
  • VB封装DLL实例(一)

    万次阅读 2017-02-11 17:30:03
    DLL即动态链接库(Dynamic Link Library),是由可被其它程序调用的函数集合组成的可执行文件模块。DLL不是应用程序的组成部分,而是运行时链接到应用程序中。 (二)主要优点: 1、多个应用程序可以共享一个DLL,...
    正 文:
    

    一、  DLL基本概念

    (一)概念

    DLL即动态链接库(Dynamic Link Library),是由可被其它程序调用的函数集合组成的可执行文件模块。DLL不是应用程序的组成部分,而是运行时链接到应用程序中。

    (二)主要优点:

    1、多个应用程序可以共享一个DLL,而且当多个应用程序调用库的同一个函数时,可执行文件中装入的只是该函数的内存地址,从而节省内存和磁盘空间;

    2、使用动态链接库易于我们维护用户程序,即使对动态链接库进行修改也不会影响用户程序;

    3、从ACCESS角度而言,还可以更好的确保核心代码的安全。

     

    二、  用VB封装VBA代码,构建自定义的DLL动态链接库

    (一)ACCESS中实例代码

    下面是一个“快速获取数字(Acc).mdb”实例(该实例在文件包中),单击“提取结果”按钮,将文本框中的数字在弹出消息显示出来。

    我将就这个实例演示如何将该实例VBA代码封装成为DLL

     

         

     

    ?按钮单击事件代码如下:

    Private Sub CmdFindnumber_Click()

      Dim strM   As String    '初始字符串

      Dim strOut  As String    '输出字符串变量

      Dim I

     

      strM = Me.Text1

     '从第一个字符向最后一个字符循环,以提取每个字符

      For I = 1 To Len(strM)

         '判断是否为09字符,是则赋值输出

          If Mid(strM, I, 1) Like "[0-9]" Then

             strOut = strOut & Mid(strM, I, 1)

          End If

      Next I

      'MsgBox函数进行输出测试

      MsgBox strOut

    End Sub

       M以上代码还不能直接用于封装,须将其修改成为公用函数(过程)

     

    (二)VB封装实例中VBA代码

    步骤一:VB编辑窗中,点菜单【文件】-【新建工程】,打开新建工程窗口

      

    步骤二:修改工程名,这即生成的DLL库名

    1修改工程名为:我的动态库

    步骤三:修改类名

    1改类名为:提取数字

    步骤四:在代码窗口输入如下代码。将ACCESS中的单击事件代码,略做修改成为一个公用函数,然后复制到VB代码编辑窗口

     

     

    ?代码如下

    '将这前的ACCESS代码改成一个公用函数

     '输入:strPutString字符串变量,需分离数字的字符串

     '输出:fFindNumber字符串变量,得到的数字字符

     Public Function fFindNumber(strPutString As String) As String

       Dim strOut  As String    '输出字符串变量

       Dim I

     

      '从第一个字符向最后一个字符循环,以提取每个字符

       For I = 1 To Len(strPutString)

         '判断是否为09字符,是则赋值输出

           If Mid(strPutString, I, 1) Like "[0-9]" Then

             strOut = strOut & Mid(strPutString, I, 1)

           End If

       Next I

      '数字输出

       fFindNumber = strOut

     End Function

     

    步骤五:编译DLL,点菜单【文件】-【生成我的动态库.dll】,VBA代码封装DLL就完成了。

     

    三、  在mdb中调用自定义DLL动态链接库

     

    (一)新建数据库及窗体

    新建【快速提取数字(DLL)实例.mdb】数据库,新建一个窗体【frmMain】,在窗体添文本框【text0】,按钮【CmdFindNum】,Caption属性:“提取数字”(见下图)

     

    (二)引用我的动态库.dll】库

    按【Alt+F11】打开VBE窗口,点菜单【工具】-【引用】,打开引用对话框,完成对我们自己编译的DLL的引用。

     

     

      

    (三)在CmdFindNum】按钮单击事件中加入如下代码。

    Private Sub CmdFindNum_Click()

      '申明自定义类

      Dim MyFindNum As提取数字

      Dim strOut As String

     

      '实例化"提取数字类"对象

      Set MyFindNum = New提取数字

      '将函数输出结果赋值给自定义字符串变量

      strOut = MyFindNum.fFindNumber(Text0)

     

      '在消息框中显示

      MsgBox "你提取的数字为:" & strOut, vbInformation, "江羽提示:"

    End Sub

     

    点击保存后,你就可以运行一下窗体测试你的成果了

    M本文实例见实例包,下载测试如果提示错误,请重新对自定义类库进行引用。

     

    本文只是通过一个简单的实例演示了,如何通过VB封装一般的VBA中代码,因为该代码中并未涉及到ACCESS应用程序对象,所以在VB中没有对ACCESS对象类库进行引用,另外实例中只是简单演示了,如何手动实现对DLL的注册引用,在后续文章中我将就如何实现DLLACCESS应用程序对接及DLL的自动注册及引用结合实例进行讲解。

     

    展开全文
  • VB封装DLL实例(二)

    千次阅读 2017-02-11 17:37:21
    上文中我们已经就DLL的基本概念,以及如何将VBA代码封装为DLL,如何引用该生成的DLL动态链接库,进行了初步的讲解,我想大家对于VB封装DLL应该有了一个初步的了解。 下面主要就DLL如何实现对ACCESS对象进行封装方法...
  • VB封装DLL实例讲解

    2010-06-28 13:23:07
    一、 DLL基本概念 (一)概念 DLL即动态链接库(Dynamic Link Library),是由可被其它程序调用的函数集合组成的可执行文件模块。DLL不是应用程序的组成部分,而是...二、 用VB封装VBA代码,构建自定义的DLL动态链接库
  • 水和蒸汽性质调用DLL,含应用实例,适用于热工编程
  • VB如何调用dll函数

    2016-07-14 10:53:12
    假如我有个DLL,名为 asdfg.dll 它里面有一个函数 zxc(参数1,参数2) 我要用这个函数,该怎么写? 1.在工程-引用中将asdfg.dll引用过来  2.dim AAA as asdfg 'asdfg是类模块的名称 Private Sub Form_Load() dim ...
  • vb.net创建和调用DLL教程

    万次阅读 2017-09-12 07:04:49
    1,创建类库项目。...然后确定下一步 2,保存项目。在Class1添加一个求和方法CalAdd ...点击菜单 生成-生成Test,编译成功后,打开我们项目保存的文件夹..\Test\Test\bin\Debug,我们可以看到里面有Dll文件,这
  • 本文通过VB5.0创建ActiveX DLL文件,这个文件模拟了一个掷色子的过程,向大家介绍如何在ASP中调用DLL的文章专题。 动态联接库(DLL)是加快应用程序关键部分的执行速度的重要方法,但有一点恐怕大部分人都不知道,那...
  • VB6.0的“工程-引用”中有很多控件供用户使用,但有时需要自己编写dll文件以实现...本文介绍如何自己编写一个简单的dll文件,然后在win7 64位下使用regsvr32注册,并在VB6.0中引用,最后在标准EXE中调用实现某些功能。
  • 动画中的原代码如下: 复制代码 代码如下: VERSION 1.0 CLASS BEGIN MultiUse = -1 ...0 ‘NotAnMTSObject END Attribute VB_Name = “tw” Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attr
  • c#调用vb dll

    热门讨论 2010-10-11 14:38:05
    本例子中实现在c#中调用vb生成的dll文件(com调用)例子由两个主要步骤组成:一 ...二 是在c#中引入步骤一中生成的dll,然后生成实例调用步骤一:首先在vb中新建一个ActiveX DLL 工程,然后在类模块文件中填入以下代

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,842
精华内容 4,336
关键字:

vb调用dll实例