精华内容
下载资源
问答
  • VBA调用C++DLL

    2021-01-06 05:44:00
    VBA调用C++DLL C++DLL示例 以下建立了一个比较简单的C++的DLL,它的输入是一个字符串,两个double类型的变量,输出double类型的计算结果。 CPP代码: #include stdafx.h #include test_excel.h double fun_property...
  • VBA 调用DLL动态链接

    2015-09-06 11:14:00
    在ArcMap中引用动态链接 我在VB6下编译生成了一个动态链接文件VBAPrj.dll,其中有一类模块VBACls,...1.打开VBA编辑器,点"工具"菜单下的"引用"命令,在引用对话框中引用该动态链接调用代码如下: Dim...

    在ArcMap中引用动态链接库
           我在VB6下编译生成了一个动态链接库文件VBAPrj.dll,其中有一类模块VBACls,此类模块有一个方法Test(Doc As Object)。
            常见的方法有三种(作者:张业新):

    1.打开VBA编辑器,点"工具"菜单下的"引用"命令,在引用对话框中引用该动态链接库。
            调用代码如下:
             Dim VBACls As New VBAPrj.VBACls
             VBACls.Test(ThisDocument) 
      2.如果知道该动态链接库文件的位置,可以在ThisDocument代码窗口以代码形式引用, 代码如下:
             Private Sub Document_Open()
                       On Error Resume Next
                       Me.VBProject.References.AddFromFile "D:\VBAPrj.dll"
             End Sub
        3.将动态链接库文件拷贝到文档同一目录下,可在ThisDocument代码窗口中建立如下引用函数:
              Private Function GetProjectDoc() As Object
                     On Error Resume Next
                     Dim VBACls As Object
                     Set VBACls = CreateObject("VBAPrj.VBACls")
                     If VBACls Is Nothing Then
                  MsgBox "VBAPrj.dll必须和文档在同一目录下!"
                  Exit Function
                    End If
                    Set GetProjectDoc = VBACls
             End Function
            然后以以下代码形式调用Test:
             Dim objPrjDoc As Object
             Set objPrjDoc = GetProjectDoc
             Call objPrjDoc.Test(ThisDocument)
             Set objPrjDoc = Nothing
           使用第一种方法调试提示找不到类库,第二种方法我没试过,用了第三种方法调试成功。我也觉得第三种还是蛮好的,除了多几行代码。成功调用DLL后原来很多在VB下写的东东简单改一下就可以在VBA下用了,效率又高,保密性又好

    转载于:https://www.cnblogs.com/lbnnbs/p/4784950.html

    展开全文
  • VBA调用Shell

    2021-08-28 15:32:59
    VBA中执行Shell介绍,打开应用,执行python、bat、mysql;

    VBA的Shell函数

    函数参数介绍

    Shell(PathName[,WindowStyle])

    • PathName :要执行的程序名,包括目录或文件夹
    • WindowStyle :程序运行时窗口的样式

    Shell函数返回在第一个参数中指定的应用程序的任务标识。可以使用这个数字在稍后激活该任务;
    Shell函数启动的应用正在运行时,VBA进程不会终止(类似于加入了进程等待)。
    Shell函数异步运行应用程序,执行Shell函数后,还有其他过程,会与Shell函数内容同时执行;
    Shell函数第二个参数WindowStyle的值:

    常量说明
    vbHide0窗口将隐藏,并且焦点将传递给隐藏窗口。
    vbNormalFocus1窗口具有焦点且还原为其原始大小和位置。
    vbMinimizedFocus2窗口将显示为具有焦点的图标。
    vbMaximizedFocus3使用焦点最大化窗口。
    vbNormalNoFocus4窗口将还原为其最新的大小和位置。 当前活动窗口仍保持活动状态。
    vbMinimizedNoFocus6窗口将显示为图标。 当前活动窗口仍保持活动状态。

    示例:

    Function shell_func(Program As String)
    ' Program:应用名称
    Dim TaskId As Double
    On Error Resume Next
    TaskId = Shell(Program, 1)      ' 任务标识,参数1代表窗口具有焦点且还原为其原始大小和位置
    If Err <> 0 Then     ' 不等于0,表示报错
        MsgBox "无法打开应用:" & Program, vbCritical, "Error"
    End If
    End Function
    

    过程等待

    Shell在运行完之后会关闭小黑窗,处理快点几乎看不见,可以通过在命令行前加入cmd /k,小黑窗不会关闭;
    Shell "cmd /k ipconfig/all"
    VBA等待shell执行完再执行后面进程,可以定义这么一个函数

    Function ShellAndWait(cmd As String) As String 
        Dim oShell As Object, oExec As Object
        Set oShell = CreateObject("WScript.Shell")
        Set oExec = oShell.exec(cmd)        ' 执行cmd命令
        result = oExec.StdOut.ReadAll    ' 读取命令行返回的内容,读完后再进行vba后面的逻辑处理;
        Set oShell = Nothing
        Set oExec = Nothing
    End Function
    

    案例-执行Windows的bat文件

    Shell "c:/bat_file.bat"


    案例-mysql查询

    Sub sql_to_txt()
    Dim this_path,cmd As String
    
    this_path = ThisWorkbook.Path    ' 当前工作簿的路径
    ChDrive "F"   '切换磁盘渠道到F盘
    ChDir this_path     ' 切换路径,这样my_ndim_table文件就写入到工作簿所在目录下了
    '-u账号 -p密码 库名mydb -e执行 "要执行的sql",> 查询结果重定向到txt文件
    cmd = "cmd /k mysql -uroot -p123456 mydb -e ""select * from d_mg_game_ndim limit 20"" > my_ndim_table.txt"
    shell cmd, 1    
    End Sub
    

    案例-执行python脚本

    ' 打开jupyter notebook
    Shell "jupyter notebook"
    
    ' 执行python脚本
    shell "python d:/python.py"
    

    案例-指定应用程序打开文件

    这里使用的是notepad++打开命名为my_ndim_table.txt的文本文件

    ChDrive "c"
    ChDir "C:\Program Files (x86)\Notepad++"
    cmd_str = ".\notepad++.exe F:\桌面\my_ndim_table.txt"
    Debug.Print cmd_str
    
    Shell cmd_str, 1
    

    AppActivate语句

    如果一个程序已经在运行,使用shell函数会启动另一个实例,大多情况下,只需要激活正在运行的实例,而并非启动另一个实例。可以通过AppActivate语句激活实例;

    Sub StartCalculate()
    Dim Program As String
    Dim TaskId As Double
    
    Program = "calc.exe"   ' 计算器
    On Error Resume Next   '遇到错误,继续执行下面语句
    AppActivate "计算器"    ' 这里是应用程序面板的标题titile
    If Err <> 0 Then   ' 如果没有捕捉到‘计算器’程序,则err不等于0
        Err = 0
        TaskId = shell(Program, 1)    ' 打开计算器
        If Err <> 0 Then MsgBox "无法打开计算器"
    End If
    End Sub
    

    激活计算器


    EXCEL表格函数打开应用程序

    如果是在工作表里,我们可以使用该函数:
    =HYPERLINK("C:\Windows\System32\calc.exe","打开计算器")
    工作表超链接打开计算器


    ShellExecute函数

    函数介绍

    使用API函数ShellExecute能打开任何文件(包括桌面和URL快捷方式)。ShellExecute解析系统注册表HKEY_CLASSES_ROOT中所有的内容,判断启动哪一个执行程序,并且启动一个新的实例或使用DDE将文件名连到一打开的实例。然后,ShellExecute返回打开文件的应用的实例句柄。
    ShellExecute函数可以简单地打开磁盘文件和Internet文件。如果将第二个参数“OPEN”改为“PRINT”或者“EXPLORE”,则ShellExecute能打印文件或打开文件夹。

    该函数只能启用已知文件名的应用程序(文件类型在Windows注册)


    函数API声明

    在模块中的声明部分输入如下声明语句
    声明

    Private Declare PtrSafe Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hWnd As Long, _
        ByVal ipOperation As String, ByVal lpFile As String, _
        ByVal lpParameters As String, ByVal lpDirectory As String, _
        ByVal nShowCmd As Long) As Long
    

    声明关键词:

    • Declare关键字:在模块级别中声明对动态链接库(DLL)中外部过程的引用。
    • Function关键字:紧随Function的函数名是从VBA中调用该函数时要使用的名称。该名称可以与API函数本身的名称相同,也可以使用Declare语句中的Alias关键字,表示要在VBA中使用不同的名称(“别名”)调用该函数。
    • Lib关键字:指明包含所声明过程或函数的动态链接库。它说明的是函数或过程从何而来的问题。例如在上例中,SetFocus Lib"user32"说明函数SetFocus来自“user32.dll”文件。
    • Alias关键字:用于指定API函数的别名,如果调用的API函数要使用字符串(参数中包含String类型),则Alias关键字是必需的。这是因为在ANSI和Unicode字符集中同一API函数的名称可能是不一样的。为了保证不出现声明错误的情况,必须使用Alias关键字指出API函数的别名。

    案例:打开网页、图片、文本、EXCEL

    Sub open_url()
    ' 打开百度链接
    res = ShellExecute(0&, vbNullString, "http://www.baidu.com", vbNullString, vbNullString, 1)
    
    ' 打开图片
    res = ShellExecute(0&, vbNullString, "F:\桌面\111.png", vbNullString, vbNullString, 1)
    
    ' 打开txt文件
    res = ShellExecute(0&, vbNullString, "my_ndim_table.txt", vbNullString, vbNullString, 1)
    
    ' 打开excel表格
    res = ShellExecute(0&, vbNullString, "F:\excel_file.xlsx", vbNullString, vbNullString, 1)
    End Sub
    
    展开全文
  • 易语言编写的dll动态链接能够被VBA宏代码函数调用,并且作为电子表格自定义函数使用,先修订其他人的整数形,以供参考
  • VBA调用Windows API的方法

    千次阅读 2019-10-08 17:52:14
    VBA中使用Windows API VBA是一种强大的编程语言,可用于自定义Microsoft Office解决方案。通过使用VBA处理一个或多个Office应用程序对象模型,可以容易地修改Office应用程序的功能或者能够使两个或多个Office应用...

    【转自“文韬武略,天下第一!”】:在VBA中使用Windows API

    VBA是一种强大的编程语言,可用于自定义Microsoft Office解决方案。通过使用VBA处理一个或多个Office应用程序对象模型,可以容易地修改Office应用程序的功能或者能够使两个或多个Office应用程序协同工作以完成单个应用程序无法完成的任务。然而,使用VBA仅能控制操作系统的一小部分。Windows API提供了控制操作系统绝大多数方面的功能。下面,介绍在VBA中使用Windows API的一些知识。

    理解APIs

    API只是一组函数,可用于处理组件、应用程序或操作系统。通常,API由一个或多个提供某种特定功能的DLLs组成。

    DLLs是包含函数的文件,能够从任何运行的Windows应用程序中调用DLLs。在运行时,DLL中的函数被动态链接到调用它的应用程序里。无论多少应用程序调用DLL中的函数,该函数仅存在于磁盘的单个文件中,并且DLL在内存中仅被创建一次。

    您可能最经常听说的API是Windows API,它包括组成Windows操作系统的DLLs。每个Windows应用程序都直接或间接地与Windows API相交互,Windows API确保运行在Windows下的所有应用程序都按一致的方式工作。

    除了Windows API外,还有其它发布的APIs可用。例如,邮件应用程序编程接口(MAPI)是一组用于编写电子邮件应用程序的DLLs。

    APIs通常是由创建Windows应用程序的C和C++程序员编写,但能够使用VBA调用DLL中的函数。因为大多数DLLs最初都是由C/C++程序员编写和文档规范,所以调用DLL函数与调用VBA函数不同。为了使用API,必需理解如何传递参数到DLL函数。

    为了调用Windows API中的函数,需要描述这些可用的函数的文档规范,如何在VBA中声明这些函数,以及如何调用它们。下面是两个有用的资源:

    1、Win32API.txt文件,包含Windows API中大多数函数的VBA Declare(声明)语句。可以使用API Viewer加载宏查找和复制需要的Declare语句。可以在下面的站点下载API声明查看器:

    http://www.activevb.de/rubriken/apiviewer/index-apiviewereng.html

    win32api.txt文件下载:

    2、Microsoft Platform SDK,包含复杂的Windows API文档。可以在下面的地址中查看:
    https://docs.microsoft.com/en-us/windows/win32/api/_winmsg/

    使用Declare语句

    在从VBA中调用DLL里的函数之前,必须为VBA提供在哪里找到函数以及如何调用该函数的信息,有两种方法:

    1、设置对DLL类型库的引用。

    2、在模块中使用Declare语句。

    设置对DLL类型库的引用是使用DLL中的函数的最容易的方法。一旦设置引用,就可以将其当作工程里的一部分一样调用DLL函数。然而,也要注意一些事项。首先,设置对多个类型库的引用会影响应用程序的性能;其次,不是所有的DLLs都提供类型库,虽然可以对没有提供类型库的DLL设置引用,但不能调用该DLL中的函数。

    注意,组成Windows API的DLLs没有提供类型库,因此不能设置对它们的引用并调用其中的函数。要调用Windows API中的函数,必须在工程里模块的声明部分包括Declare语句。

    Declare语句是一个定义,告诉VBA在哪里找到特定的DLL函数以及如何调用该函数。在代码中添加Declare语句最简单的办法是使用API Viewer加载宏,其中包含Windows API中大多数函数的Declare语句,也包含一些函数所需要的常量和类型定义。

    Declare语句声明的形式如下:

    [Public|Private]Declare Sub name Lib "libname" [Alias "aliasname"][([arglist])]
    [Public|Private]Declare Function name Lib "libname" [Alias "aliasname"] [([arglist])] [As type]
    

    下面是GetTempPath函数的Declare语句的示例,该函数返回Windows临时文件夹的路径(默认为C:\Windows\Temp):

    Private Declare Function GetTempPath Lib "kernel32" _
    Alias "GetTempPathA" (ByVal nBufferLength As Long, _
    ByVal lpBuffer As String) As Long
    

    关键字Declare告诉VBA在工程中要包含的DLL函数的定义。在标准模块中的Declare语句可以是公共的或私有的,取决于你希望API函数仅用于单个模块还是整个工程。在类模块中,Declare语句必须是私有的。

    在关键字Function之后是函数的名字,具体地说,是从VBA中调用该函数时使用的名字。这个名字可以与API函数本身的名字相同,也可以在Declare语句中使用关键字Alias指定打算在VBA中通过不同的名字(别名)调用该函数。

    在上面的示例中,在DLL中API函数的名字是GetTempPathA,从VBA中调用该函数时使用的名字是GetTempPath。注意,DLL函数的实际名字出现在关键字Alias之后,同时也注意到GetTempPath是Win32API.txt文件用于该函数的别名,但你可以将其改变为任何你想要的名字。

    下面是为什么要在Declare语句中使用别名的一些理由:

    • 一些API函数的名字以下划线(_)开始,在VBA中是不合乎语法的。为了从VBA中调用该函数,需要使用别名。
    • 因为别名允许将DLL函数命名为你所希望的名字,所以可以使函数名字遵循你自已在VBA中的命名标准。
    • 因为API函数是区分大小写的,而VBA函数则不,所以可以使用别名来改变函数名的大小写。
    • 一些DLL函数带有接受不同数据类型的参数,这些函数的VBA声明语句定义这些参数为类型Any,调用带有声明为Any的参数的DLL函数是危险的,因为VBA不会执行任何数据类型检查。如果想避免传递类型为Any的参数的危险,可以声明相同的DLL函数的多个版本,每一个都具有不同的名字和不同的数据类型。
    • Windows API为所有接受字符串参数的函数都包含两个版本:ANSI版和Unicode版。ANSI版带有"A"后缀,正如上例所示,而Unicode版带有"W"后缀。虽然VBA使用Unicode,但在调用DLL中的函数之前,它将所有的字符串转换为ANSI字符串,因此在从VBA中调用Windows API函数时通常使用ANSI版。API Viewer加载宏自动为所有接6受字符串参数的函数命名别名,因此可以不必包含"A"后缀而调用该函数。

    关键字Lib指定包含函数的DLL。注意,在声明语句里以字符串形式包含DLL的名字。如果在系统中没有找到关键字Lib之后指定的DLL,对该函数的调用将失败,导致运行时错误:48,装载DLL错误。因为可以在VBA代码中处理这种错误,所以可以编写健壮的代码得体地处理错误。

    下面列出了Windows API中最常使用的DLLs:

    • Kernel32.dll:低级别的操作系统函数,例如内存管理和资源处理。
    • User32.dll:Windows管理函数,例如消息处理、计时器、菜单和通讯。
    • GDI32.dll:图像设备接口(GDI)库,包含设置输出的函数,例如绘图、显示上下文和字体管理。

    大多数DLLs,包括Windows API中的DLLs,都采用C/C++编写,因此,传递参数到DLL函数需要参数的理解以及C/C++接受的数据类型,而这些不同于VBA函数。

    同时,DLL函数的许多参数按值传递。默认情况下,VBA中的参数按引用传递。因此,当DLL函数需要按值传递的参数时,在函数定义中包括关键字ByVal是必要的。在函数定义中忽略ByVal关键字可能会在应用程序中导致无效的页错误。有时,可能会发生VBA运行时错误:49,坏的DLL调用协议。

    按引用传递参数传递该参数的内存位置到被调用的过程,如果该过程修改了参数的值,那么会修改该参数的唯一的副本,因此,当返回到调用过程时,参数包含的是修改后的值。

    按值传递参数到DLL函数,将传递该参数的副本,函数操作该参数的副本,避免了修改实际参数的内容。当返回到调用过程时,该参数包含与调用其它过程前相同的值。

    因为按引用传递允许在内存中修改参数值,如果不恰当地按引用传递参数,DLL函数可能会覆盖它不应该覆盖的内存,导致错误或者不可预料的结果。Windows维护许多值不应该被覆盖,例如,Windows为每个窗口赋惟一的32位标识符,称作句柄(handle)。句柄总是按值传递给API函数,因为如果Windows修改了某窗口的句柄,那么不再能够追踪到该窗口。(虽然关键字ByVal出现在String类型的一些参数前面,但是字符串总是按引用被传递到Windows API函数)

    上述声明语句接受两个参数,一个为Long型,另一个为String型,并返回一个Long型值。

    使用常量

    除了DLL函数的声明语句外,一些函数还需要定义常量以及在函数中使用的类型。在模块的声明部分包括常量和用户定义类型。

    如何知道函数需要的常量和用户定义类型呢?需要查看该函数的文档。Win32API.txt文件包含函数的常量和用户定义类型的定义。可以使用API Viewer加载宏找出这些常量和用户定义类型,并将它们复制到代码中。不巧的是,常量和用户定义类型不会以任何方式与需要它们的声明语句相联系,因此,仍然需要检查DLL函数的文档,决定哪个常量和类型与哪个声明语句匹配。

    函数可能需要传递常量来指明想要函数返回的信息。例如,GetSystemMetrics函数接受75个常量,每一个都指定操作系统的不同方面,该函数返回的信息取决于传递给它的常量。要调用GetSystemMetrics,不需要包括所有的75个常量,只需包括要使用的就可以了。

    建议定义常量而不是简单地传递它们代表的值。Microsoft确保在将来的版本中仍然会保留相同的常量,但不保证常量的值相同。

    DLL函数需要的常量通常是隐含的,因此需要查阅函数的文档来确定传递的常量,以返回特定的值。

    在《Professional Excel Development》中介绍了如何查找常量的值的方法。即在Microsoft的站点下载并安装核心SDK软件包,其中有一个名为"include"的子目录,所有用于创建动态链接库(DLL)的C++头文件都存放在这个目录中。通过搜索就能找到常量所在的文件,例如查找SM_CXSCREEN,会返回文件"winuser.h",打开该文件查询就可找到相关的常量。

    下面的示例是包括GetSystemMetrics函数的声明语句,接受两个常量,然后展示如何从属性过程中调用GetSystemMetrics,以像素为单位返回屏幕的高度。

    Declare Function GetSystemMetrics Lib "User32" (ByVal nIndex As Long) As Long
    Const SM_CXSCREEN As Long = 0 '屏幕宽度
    Const SM_CYSCREEN As Long = 1 '屏幕高度
    
    Public Property Get ScreenHeight() As Long
    '以像素为单位返回屏幕的高度
    ScreenHeight = GetSystemMetrics(SM_CYSCREEN)
    End Property
    
    Public Property Get ScreenWidth() As Long
    '以像素为单位返回屏幕的宽度
    ScreenWidth = GetSystemMetrics(SM_CXSCREEN)
    End Property
    

    使用用户定义类型

    用户定义类型是一种数据结构,可以存储多个相关的不同类型的变量,与C/C++中的结构一致。有时,传递空的用户定义类型到DLL函数,函数填充值;有时,从VBA填充用户定义类型,并将其传递给DLL函数。

    可以将用户定义类型作为一箱抽屉,每个抽屉可以包含不同类型的项目,但将它们组合在一起可以当作相关项目的单个箱子。可以从任何抽屉获得项目而不必担心存储在任何其它抽屉中的项目。

    要创建用户定义类型,使用Type … End Type语句。在Type…End Type语句里,列出了每个项目,包含值和数据类型。用户定义类型的元素可以是数组。

    下面的代码段展示如何定义RECT用户定义类型,和管理屏幕矩形块的几个Windows API函数一起使用。例如,GetWindowRect函数接受RECT类型的数据结构,使用关于窗口的左侧、顶部、右侧和底部位置的信息填充。

    Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
    End Type
    

    要传递用户定义类型到DLL函数,必须创建该类型的变量。例如,如果打算传递RECT类型的用户定义类型到DLL函数,那么就要包括变量声明,如下所示:

    Private rectWindow As RECT
    

    可以引用用户定义类型里的单个元素,如下所示:

    Debug.Print rectWindow.Left
    

    使用句柄

    调用DLLs中的函数之前需要理解的另一个重要的概念是句柄(handle)。简单地说,句柄是32位正整数,Windows用于识别窗口或另一个对象,例如字体或位图。

    在Windows中,窗口有许多不同的表现形式。事实上,在屏幕中看到的几乎所有事情都在窗口里,并且不能看到的大多数事情也在窗口里。窗口能够是一个绑定的屏幕矩形区域,就像您习惯看到的应用程序窗口一样。窗体中的控件,例如列表框或滚动条,也都是窗口,虽然不是所有类型的控件都是窗口。在桌面上显示的图标以及桌面本身,都是窗口。

    因为所有这些类型的对象都是窗口,所以Windows能够相同地对待它们。Windows提供给每个窗口一个唯一的句柄,并使用该句柄去处理窗口。许多API函数返回句柄或者接受句柄作为其参数。

    当窗口创建时Windows赋句柄给该窗口,当窗口销毁时Windows释放该句柄。虽然句柄保留的时间与窗口存在的时间相同,但不保证一个窗口在销毁并重新创建后有相同的句柄。因此,如果在变量中存储句柄,那么记住该窗口销毁后,该句柄不再有效。

    GetActiveWindow函数是返回窗口句柄的函数示例,此时,应用程序窗口是当前活动的窗口。GetWindowText函数接受某窗口的句柄,并且如果窗口有标题的话返回该窗口的标题。下面的程序使用GetActiveWindow返回活动窗口的句柄,GetWindowText返回其标题:

    Declare Function GetActiveWindow Lib "user32" () As Long
    Declare Function GetWindowText Lib "user32" _
    Alias "GetWindowTextA" (ByVal Hwnd As Long, _
    ByVal lpString As String, ByVal cch As Long) As Long
     
    Function ActiveWindowCaption() As String
    Dim strCaption As String
    Dim lngLen As Long
    '创建使用空字符填充的字符串
    strCaption = String$(255, vbNullChar)
    '返回字符串的长度
    lngLen = Len(strCaption)
    '调用GetActiveWindow来返回活动窗口的句柄
    '与字符串和其长度一起,传递句柄到GetWindowText
    If (GetWindowText(GetActiveWindow, strCaption, lngLen) > 0) Then
    '返回Windows已写入的值给字符串
    ActiveWindowCaption = strCaption
    End If
    End Function
    

    GetWindowText函数接受三个参数:窗口的句柄、将返回窗口标题里的空结尾的字符串、以及字符串的长度。

    下面列出了Excel中常用的窗口类名称:

    • Excel主窗口:XLMAIN
    • Excel桌面:XLDESK
    • Excel工作表:EXCEL7
    • Excel用户窗体:ThunderDFrame(Excel 2000以后版本)、ThunderRT6DFrame(Excel 2000以后版本,用于作为COM加载项时)、ThunderXFrame(Excel 97)
    • Excel状态栏:EXCEL4
    • Excel图表窗口:EXCELE(Excel2007以前版本)

    FindWindow函数使用类名和窗口标题查找窗口。下面的代码以像素为单位查找Excel主窗口的位置和大小:

    '包含窗口大小的用户定义类型
    Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
    End Type
    
    '查找窗口的API函数
    Declare Function FindWindow Lib "user32" _
    Alias "FindWindowA" ( _
    ByVal lpClassName As String, _
    ByVal lpWindowName As String) As Long
     
    '获取窗口大小的API函数
    Declare Function GetWindowRect Lib "user32" ( _
    ByVal hWnd As Long, _
    lpRect As RECT) As Long
     
    Sub ShowExcelWindowSize()
    Dim hWnd As Long, uRect As RECT
    '获取Excel主窗口的句柄
    'Excel 2002及以后版本也可使用hWnd=Application.Hwnd
    hWnd = FindWindow("XLMAIN", Application.Caption)
    '将窗口大小信息存入到RECT结构中
    GetWindowRect hWnd, uRect
    '显示结果
    MsgBox "这个Excel窗口的尺寸为:" & _
    vbCrLf & "左侧:" & uRect.Left & _
    vbCrLf & "右侧:" & uRect.Right & _
    vbCrLf & "顶部:" & uRect.Top & _
    vbCrLf & "底部:" & uRect.Bottom & _
    vbCrLf & "宽度:" & (uRect.Right - uRect.Left) & _
    vbCrLf & "高度:" & (uRect.Bottom - uRect.Top)
    End Sub
    

    调用函数

    虽然调用DLL函数的许多方式与调用VBA函数相似,但是开始时有一些不同可能会使DLL函数混淆。下面将介绍如何输入DLL函数中的参数并加前缀、如何返回字符串、如何传递数据结构、能够接受什么返回值、以及如何获取错误信息。

    参数数据类型

    在C/C++中使用的数据类型、用于描述它们的标记都不同于在VBA中的用法,下面描述了DLL函数中常用的数据类型以及它们在VBA中的等效表示。

    C/C++数据类型匈牙利前缀描述等效的VBA表示
    BOOLb8位布尔值。0表示False;非0表示TrueBoolean或Long
    BYTEch8位无符号整数Byte
    HANDLEh32位无符号整数,代表Windows对象的句柄Long
    intn16位符号整数Integer
    longl32位符号整数Long
    LPlp32位对内存中C/C++结构、字符串、函数或其它数据的长指针Long
    LPZSTRlpsz32位对C类型空结尾字符串的长指针Long

    虽然您应该熟悉这些数据类型和前缀,但前面提到的Win32API.txt文件包含了准备在VBA中使用的声明语句。如果在代码中使用这些声明语句,那么函数参数已经定义了正确的VBA数据类型。

    在《Excel 2007 VBA参考大全》的第27章,详细介绍了如何将C-样式声明转换为VBA声明语句。

    只要已经定义并传递了正确的数据类型,调用DLL函数与调用VBA函数采取相同的方法。当然也有例外,这将在下面的内容中介绍。

    从DLL函数中返回字符串

    DLL函数不会以VBA函数相同的方法返回字符串。因为字符串总是按引用传递到DLL函数,DLL函数能够修改字符串参数的值。宁可返回字符串作为函数的返回值,就像可能在VBA中做的那样,DLL函数返回字符串到传递给该函数的String类型的参数。函数的实际返回值经常是一个长整型值,指定写入到字符串参数的字节数量。

    接受字符串参数的DLL函数获得指针,指向内存中该字符串的位置。指针只是内存地址,表明在哪里存储字符串。因此,当从VBA中传递字符串到DLL函数时,传递给DLL函数一个指针,指向内存中的字符串。接着,这个DLL函数修改存储在那个地址的字符串。

    要调用写到String变量的DLL函数,需要采取额外的步骤合适地格式字符串。首先,String变量必须是空结尾字符串。一个空结尾字符串以特定的空字符结束,空字符通过VBA常量vbNullChar来指定。

    其次,DLL函数不能修改已经创建的字符串的大小。因此,需要确保传递给函数的字符串足够大以容纳整个返回值。当传递字符串到DLL函数中时,通常需要指定在另一个传递的参数中字符串的大小。Windows追踪字符串的长度,以确保不会覆盖掉字符串已使用过的内存。

    传递字符串到DLL函数中的一个好方法是创建String变量,并使用String$函数在其中填充空字符,使其足够大以容纳函数返回的字符串。例如,下面的代码创建一个144字节长的字符串,并使用空字符串填充:

    Dim strTempPath As String
    strTempPath = String$(144, vbNullChar)
    

    当传递字符串到DLL函数中时,如果不知道字符串的长度,那么可以使用Len函数确定其长度。

    获取Windows临时文件夹的GetTempPath函数,就是返回String值的DLL函数的例子。该函数接受两个参数,一个空结尾的字符串变量和一个包含字符串长度的数值变量。修改该字符串以便包含路径,例如C:\Temp\。(Windows需要一个临时文件夹存在,于是该函数应该总是返回该文件夹的路径。如果由于某种原因不存在临时文件夹,GetTempPath返回0)。

    下面的程序调用GetTempPath函数获取Windows临时文件夹的路径:

    Declare Function GetTempPath Lib "kernel32" Alias "GetTempPathA" _
    (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long
    
    Property Get GetTempFolder() As String
    '返回用户临时文件夹的路径.
    '对于根目录,Windows需要一个临时文件夹存在
    '因此应该总是返回其路径
    '以防万一,检查GetTempPath的返回值
    Dim strTempPath As String
    Dim lngTempPath As Long
    '使用空字符填充字符串
    strTempPath = String(144, vbNullChar)
    '获得字符串的长度
    lngTempPath = Len(strTempPath)
    '调用GetTempPath,传递字符串长度和字符串
    If (GetTempPath(lngTempPath, strTempPath) > 0) Then
    'GetTempPath返回路径到字符串中.
    '截去字符串开始的空字符
    GetTempFolder = Left(strTempPath, InStr(1, strTempPath, vbNullChar) - 1)
    Else
    GetTempFolder = ""
    End If
    End Property
    

    注意,当传递字符串到函数中时,使用空字符填充该字符串。函数写入返回的字符串值"C:\Temp"到字符串变量的第一部分中,并且剩下的保留空字符填充,接着使用Left函数截取字符串。

    GetTempPath函数的实际返回值是已经被写到字符串变量中的字符数。如果返回的字符串是"C:\Temp",那么GetTempPath函数返回8。

    注意,这仅对从函数返回字符串时传递空结尾字符串及其大小是必需的。如果函数不返回字符串到字符串参数中,而是接受对函数指定信息的字符串,那么只需传递正常的VBA字符串变量。

    传递用户定义类型到DLL函数

    许多DLL函数需要通过使用预定义的格式传递数据结构。当从VBA中调用DLL函数时,根据函数的需求传递已经定义的用户定义类型。

    通过查看函数的声明语句,您能够理解什么时候需要传递用户定义类型以及需要在代码中包括哪种类型定义。需要数据结构的参数总是被声明为长指针:指向内存中数据结构的32位数字值。为长指针参数约定的前缀是"lp"。此外,参数的数据类型是数据结构的名称。

    例如,看看GetLocalTime函数和SetLocalTime函数的声明语句:

    Private Declare Sub GetLocalTime Lib "kernel32" _
    (lpSystem As SYSTEMTIME)
    Private Declare Function SetLocalTime Lib "kernel32" _
    (lpSystem As SYSTEMTIME) As Long
    

    两个函数都接受SYSTEMTIME类型的参数,即包含日期和时间信息的数据结构。下面是SYSTEMTIME类型的定义:

    Private Type SYSTEMTIME
    wYear As Integer
    wMonth As Integer
    wDayOfWeek As Integer
    wDay As Integer
    wHour As Integer
    wMinute As Integer
    wSecond As Integer
    wMilliseconds As Integer
    End Type
    

    要将数据结构传递给函数,必须声明SYSTEMTIME类型的变量,如下所示:

    Private sysLocalTime As SYSTEMTIME
    

    当调用GetLocalTime时,传递SYSTEMTIME类型的变量到该函数,并且使用表示当前本地的年、月、日、星期几、小时、分、秒、毫秒的数字值填充该数据结构。例如,下面的Property Get程序调用GetLocalTime返回表明当前小时的值:

    Public Property Get Hour() As Integer
    '返回当前时间,然后返回小时
    GetLocalTime sysLocalTime
    Hour = sysLocalTime.wHour
    End Property
    

    当调用SetLocalTime时,也传递了SYSTEMTIME类型的变量,但首先提供数据结构的一个或多个元素的值。例如,下面的Property Let程序设置本地系统时间的小时值。首先,调用GetLocalTime函数获取本地时间的当前值到数据结构中,然后使用传递给属性过程的值更新数据结构的sysLocalTime.wHour的值。最后,调用SetLocalTime函数,传递相同的数据结构,包含通过GetLocalTime加新小时值而取得的值。

    Public Property Let Hour(intHour As Integer)
    '获取当前时间以便所有值都是当前的
    '然后设计本地时间的小时部分
    GetLocalTime sysLocalTime
    sysLocalTime.wHour = intHour
    SetLocalTime sysLocalTime
    End Property
    

    GetLocalTime函数和SetLocalTime函数与GetSystemTime函数和SetSystemTime函数相似。主要的不同在于,GetSystemTime函数和SetSystemTime函数表达的时间为格林威治标准时间。例如,如果本地时间是午夜12时,而您居住在西海岸,那么格林威治标准时间就是上午8时,有8小时的时差。GetSystemTime函数返回当前时间即8:00 A.M,而GetLocalTime返回午夜12:00。

    理解Any数据类型

    一些带有一个参数的DLL函数可以接受多个数据类型。在DLL函数的声明语句中,这样的参数被声明为类型Any。VBA允许传递任何数据类型到这个参数。然而,DLL函数可能被设计为接受仅仅两个或三个不同的数据类型,因此传递错误的数据类型可能会导致应用程序错误。

    通常,当在VBA工程中编译代码时,VBA对传递给每个参数的值执行类型检查。也就是说,确保传递的值的数据类型与函数定义中的参数的数据类型相匹配。例如,如果参数定义为Long型,而试图传递String型的数值,则会发生编译时错误。这适用于调用内置的VBA函数、用户定义函数、或者DLL函数。当将参数声明为类型Any时,不会进行类型检查,因此当传递值到这种类型的参数时应该谨慎。

    一些具有一个参数的DLL函数可以接受字符串或者指向字符串的空指针。指向字符串的空指针是一个特别的指针,指令Windows忽略所给的参数。它与零长度字符串("")不同。在VBA的早期版本中,程序员必须声明参数为类型Any,或者声明DLL函数的两个版本,即一个版本定义参数类型为String,一个版本定义参数类型为Long。现在VBA包括vbNullString常量,代表指向字符串的空指针,这样可以声明参数为String类型,并且在需要传递空指针的情形下传递vbNullString常量。

    获取错误信息

    DLL函数中发生的运行时错误的行为不同于VBA中的运行时错误,即没有错误消息框显示。当运行时错误发生时,DLL函数返回某值表时发生了错误,而且错误不会中断VBA代码的执行。

    Windows API中的一些函数存储运行时错误的错误信息。如果使用C/C++编程,可以使用GetLastError函数获取关于发生的最后一次错误的信息。然而,从VBA中,GetLastError函数可能返回不确切的结果。要从VBA获得关于DLL错误的信息,可以使用VBA的Err对象的LastDLLError属性。LastDLLError属性返回发生的错误号。

    为了使用LastDLLError属性,需要知道与错误相对应的错误号。在Win32API.txt文件没有这方面的可用信息,而Microsoft Platform SDK中可以找到。

    下面的示例展示在已经调用了Windows API中的函数后如何使用LastDLLError属性。PrintWindowCoordinates程序接受窗口句柄,并调用GetWindowRect函数。GetWindowRect使用组成窗口的矩形的边的长度填充RECT数据结构。如果传递了无效的句柄,将发生错误,并且可以通过LastDLLError属性获得错误号。

    Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, _
    lpRect As RECT) As Long
     
    Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
    End Type
     
    Const ERROR_INVALID_WINDOW_HANDLE As Long = 1400
    Const ERROR_INVALID_WINDOW_HANDLE_DESCR As String = "无效的窗口句柄."
     
    Sub PrintWindowCoordinates(hwnd As Long)
    '以像素为单位打印窗口左侧,右侧,顶部和底部位置
    Dim rectWindow As RECT
    '传递窗口句柄和空的数据结构
    '如果函数返回0,那么错误就发生了
    If GetWindowRect(hwnd, rectWindow) = 0 Then
    '因为传递了无效的句柄
    '所以如果发生错误则检查LastDLLError并显示对话框
    If Err.LastDllError = ERROR_INVALID_WINDOW_HANDLE Then
    MsgBox ERROR_INVALID_WINDOW_HANDLE_DESCR, _
    Title:="错误!"
    End If
    Else
    Debug.Print rectWindow.Bottom
    Debug.Print rectWindow.Left
    Debug.Print rectWindow.Right
    Debug.Print rectWindow.Top
    End If
    End Sub
    

    要获得活动窗口的坐标,可以通过使用GetActiveWindow函数返回活动窗口的句柄,并将结果传递到前面示例定义的过程中。要使用GetActiveWindow函数,包括下面的声明语句:

    Declare Function GetActiveWindow Lib "user32" () As Long
    

    输入下面的过程后运行:

    Sub test()
    PrintWindowCoordinates (GetActiveWindow)
    End Sub
    

    要生成一条错误消息,随便使用一个长整型数值调用这个过程。

    参考资源:

    David Shank,《Office VBA and the Windows API》

    《Excel 2007 VBA参考大全》

    《Professional Excel Development》

    《VBA and Macros for Microsoft Excel》

    展开全文
  • VBA调用C/C++ DLL

    千次阅读 2011-08-08 15:04:54
    在VBA开发过程中,为了能够使用系统已经提供的函数,或已经用C++语言开发的功能,本文对VBA调用C++ DLL进行了总结。 1. 函数声明 Function prototype: DWORD WINAPI GetCurrentDirectory(  __i

    在VBA开发过程中,为了能够使用系统已经提供的函数,或已经用C++语言开发的功能,本文对VBA调用C++ DLL进行了总结。

    1.        函数声明

    Function prototype:

    DWORD WINAPI GetCurrentDirectory(

     __in   DWORD nBufferLength,

     __out  LPTSTR lpBuffer

    );

    函数声明如下:

    Public Declare Function GetCurrentDirectoryLib "kernel32" Alias "GetCurrentDirectoryA" (ByValnBufferLength As Long, ByVal lpBuffer As String) As Long

    Public  用于声明对所有模块中的所有其它过程都可以使用的函数。  Private   用于声明只能在包含该声明的模块中使用的函数。 

    Lib包含所声明函数的动态链接库名或代码资源名。    

    Alias 表示将被调用的函数在动态链接库(DLL)   中还有另外的名称。

     

    2.        DLL的位置

    DLL文件必须位于以下三个目录之一:

    (1)Windows的系统目录:\Windows\system32

    (2)DOS中path所指出的任何目录

    (3)Windows XP系统下:C:\Documentsand Settings\%USERNAME%\My Documents

    为了VBA可以调用DLL中的函数,必须把DLL放在以上三个位置中的任何一个。

    有两种办法可以解决这个问题:

    1.      在调用DLL 函数之前,把DLL copy到以上三个目录中的任何一个。

    Dim fso AsObject

    Dim dllFileNameAs String

    dllFileName = Environ("SYSTEMROOT")+ “\system32\LicenseVerify.dll”

    Set fso =CreateObject("Scripting.FileSystemObject")

    Iffso.FileExists(dllFileName) = False Then

    fso. CopyFile ThisWorkbook.Path + “\ LicenseVerify.dll”,dllFileName

    End If

     

    2.      改变当前进程的当前路径。

    例如,DLL与当前EXCEL文件放在同一个目录下,这个时候当前进程的当前路径设置如下:

    Public Declare Function SetCurrentDirectoryLib "kernel32" Alias "SetCurrentDirectoryA" (ByVallpPathName As String) As Long ‘ function declaration

    SetCurrentDirectory (ThisWorkbook.Path) ‘set the current directory to Thisworkbook.Path

    OK,这样设置后,就可以访问DLL了。这样方便程序的发布,不需要用户把DLL复制到系统目录下。

     

    3.        返回值

    如果返回值是字符串,需要在函数调用前为字符串分配内存空间。

    Public Declare Function GetCurrentDirectoryLib "kernel32" Alias "GetCurrentDirectoryA" (ByValnBufferLength As Long, ByVal lpBuffer As String) As Long

    其中,lpBuffer 是输出参数。

    例子:

    Public Declare Function GetCurrentDirectoryLib "kernel32" Alias "GetCurrentDirectoryA" (ByValnBufferLength As Long, ByVal lpBuffer As String) As Long

     

    Private Sub DoVerify()

        Dimresult As Integer

        Dim retValue AsString

     

        retValue = String(1024, vbNullChar) 'allocate the buffer for out parameter.

        result= GetCurrentDirectory(1024, retValue)   

    End Sub


    展开全文
  • VBA过程调用和参数传递

    千次阅读 2020-07-28 11:05:23
    之前在CSDN的blink发了个动态,大致意思是要认真学好VBA,以后做表什么的会方便快捷点。然后一些小伙伴就留言建议我学python,毕竟python是近两年冉冉升起的新星,VBA没有什么大的前途,学起来浪费时间。 然后昨天...
  • QT下dll动态库的制作 错误记录 VB调用时的错误记录 参考前言在MSVC下编写一个Windows dll库供调用是非常简单的,VS会给你创建好一个模板工程。但使用MinGW创建一个window dll则有点小麻烦。下面就简单说下如何使用...
  • VBA 64 32 调用dll的区别

    千次阅读 2017-03-09 15:22:05
    在这些情况下,最好选择使用一个嵌入动态链接 (DLL) 文件中的外部函数。为此,可在 VBA 中使用  Declare  语句进行 API 调用。 注释: Microsoft 提供了一个 Win32API.txt 文件...
  • DLL文件制作与在VBA调用初级进阶

    千次阅读 2008-12-26 21:37:00
    DLL文件制作与在VBA调用初级进阶■ 作者:ldhyob 日期:2004.02本文用一简例来演示如何将一正常在VBA中运行的代码移植到VB制作的DLL中去,并介绍如何在VBA中进行调用。这仅仅是最初级的介绍,希望能对此内容想了解的...
  • EXCEL VBA 二维码DLL

    2020-11-23 17:14:48
    excel VBA VB 可调用的生成二维码DLL,支持中文 调用方法: 先用regsvr32 注册DLL再调用 Public Function ShowQrcode(ByVal S As String) Dim Img As New Qrcode.cMain If Trim(S) = "" Then Exit Function End If...
  • 本机win7系统,调用华视读卡器正常&#...将动态库等文件拷贝到其他机器上后,一直提示 “错误453 找不到输入点CVR_INITCOMM 在termb.dll”  所需文件全都打包拷贝过去了,读卡器驱动也安装正常</p>
  • 我自己用VBA编写的Solidworks API实例,在SolidWorks 2010版本上调试通过。学习Solidworks VBA编程的另外一种方法是,使用Solidworks API帮助,在索引中输入VBA,可以查询到帮助文件中的VBA编程示例。 Solidworks...
  • 如果需要使用在C#或VB.Net创建一个DLL在Access VBA时,Excel VBA,或用于生产的机器上VB6应用程序,本文向您介绍如何注册,参考,并调用VBA或VB编辑器中的DLL。 你已经正确安装并注册组件? 首先,你需要获取...
  • VBA常用代码合集

    千次阅读 多人点赞 2020-12-19 11:13:11
    VBA常用代码模版Tp0️⃣—零零散散小功能(持续更新)Tp1️⃣—输出活动页面筛选后的行数Tp2️⃣—创建数组存放数据Tp2-1 静态数组Tp2-2 动态数组Tp3️⃣ 创建字典存放数据Tp4️⃣ 优化代码运行速度 Tp0️⃣—零零散...
  • as a worksheet function, we call the VBA function squareOnWorksheet. The VBA function squareOnWorksheet takes a double as an argument, calls squareForEXL using this double as an argument, and then...
  • 精彩编程与编程技巧-VB调用C程序动态链接的方法 ...
  • QT编写DLL给外部程序调用示例 方法 参数 返回值 事件回调 : 1:C#或者 NET 第一步:引入命名空间; using System Runtime InteropServices; 第二步:声明函数 必须与实际DLL中函数名及参数一致; [DllImport "qt...
  • EXCEL编程VBA高级教程

    2015-04-16 11:40:55
    一、VBA语言基础...................................................................................................................1 第一节标识符...........................................................
  • access vba动态创建控件

    千次阅读 2014-03-21 11:58:25
    VBA编写的程序中,如果可以在运行时动态创建控件无疑是非常有用的,因为有些控件是无法预先得知的,这样就更必须在运行时动态创建然后设置相应控件属性。在vb,excel当中,使用Control的add方法可以实现,然而,在...
  • VBA API详解

    千次阅读 2020-12-17 22:27:10
    Office版本变动  在office2010以前微软只提供了32-bit版本,但在2010以后出现了32-bit和64-bit两个版本...后者能处理更大的数据和VBA代码运行速度得到提升。代码的兼容性也变得复杂,特别是API的声明。 版本区别 ...
  • 目前以lib后缀的库有两种,一种为静态链接库(StaticLibary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(ImportLibary,以下简称“导入库”)。静态库是一个或者多个obj文件的打包,...
  • 用VC2013制作了个生成二维码的DLL,目的是给VBA调用。 本机编译后,运行正常。但在目标机器上却提示“VBA错误信息:找不到指定的文件“xxx.dll”(错误 53)”。 然后换用CodeBlocks+MingW编译,运行正常。 当时想...
  • 转载地址:http://www.cnblogs.com/chio/archive/2008/08/05/1261296.html 目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称"静态库"),另一种为动态连接库(DLL,以下简称"动态库")的导入库...
  • 在这些情况下,最好选择使用一个嵌入动态链接 (DLL) 文件中的外部函数。为此,可在 VBA 中使用 Declare 语句进行 API 调用。 转存失败 重新上传 取消 注释: Microsoft 提供了一个 Win32API.txt ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,864
精华内容 745
关键字:

vba调用动态库