Excel is probably one of the most used tools in this world, so the demand for integrations with extremely complex spreadsheets is a recurring scenario. APIs allow easy access to information in systems, which is becoming more and more standard in the market, with that in mind, some demands of connecting to systems via API in Excel are necessary and very useful, so I decided to share a little how I created this integration. Let’s learn how to query Rest APIs using VBA and convert the result to JSON for use in the spreadsheet.
This article hopes you will know the basics of Excel and VBA, as well as what is an API and how it works . Our goal will be to consult a Public Pokemon API and list the result in the tab results
.
Creating a blank worksheet
First let’s create a blank sheet with macro enabled, inside it I’ll create a tab called results
.
Creating the macro to query the API
by shortcut alt + f11
let’s open the Excel macro editor, and create a module called list pokemons
.
Importing the VBA-JSON library
As the API we’re going to query returns a JSON as an answer we will need to import the library VBA JSON , it will take care of all the boring work of translating the JSON and returning as an array and object. Installation is very simple, just download the latest version here and in the macro editor go to File > Import File > JsonConverter.bas
.
Enabling Microsoft Scripting Runtime
We also need to enable Microsoft Scripting Runtime, to do this just browse Tools > References
and search and enable in the list the Microsoft Scripting Runtime
.
Creating the VBA macro to query the REST API
Below is the complete code for our request, it might sound scary, but don’t worry, I’ll explain what each part is doing:
Sub listPokemons() Dim json As String Dim jsonObject As Object, item As Object Dim i As Long Dim ws As Worksheet Dim objHTTP As Object 'We selected our results sheet Set ws = Worksheets("results") 'We create our request object and send Set objHTTP = CreateObject("WinHttp.WinHttpRequest.5.1") URL = "https://pokeapi.co/api/v2/pokemon" objHTTP.Open "GET", URL, False objHTTP.Send strResult = objHTTP.responseText json = strResult Set objectJson = JsonConverter.ParseJson(json) 'We create the header cells ws.Cells(1, 1) = "name" ws.Cells(1, 2) = "link" 'We loop the results property of the API response i = 2 'We will start the counter on line 2 For Each pokemon InJsonObject("results") ws.Cells(i, 1) = pokemon("name") ws.Cells(i, 2) = pokemon("url") i = i + 1 next End Sub
First we define all the variables that we will use in our scripts, including the VBA JSON library import that we previously imported into our project.
Dim json As String Dim jsonObject As Object, item As Object Dim i As Long Dim ws As Worksheet Dim xmlhttp As Object Set xmlhttp = CreateObject("MSXML2.serverXMLHTTP") Dim objHTTP As Object
Then we select the spreadsheet we want to display the results of the API query, in our case Worksheets("results")
and then we create an object that will allow us to make the request to the API. https://pokeapi.co/api/v2/pokemon
. We’ll take the answer and put it in the variable json
, for now it is nothing more than a text.
'We selected our results sheet Set ws = Worksheets("results") 'We create our request object and send Set objHTTP = CreateObject("WinHttp.WinHttpRequest.5.1") URL = "https://pokeapi.co/api/v2/pokemon" objHTTP.Open "GET", URL, False objHTTP.Send strResult = objHTTP.responseText json = strResult
Here where the magic happens, the function parsejson
from the VBA JSON library converts the text of our variable json
for an accessible object in our script. We are now able to access all properties programmatically in our code.
Set objectJson = JsonConverter.ParseJson(json)
Now that we have the result of our API accessible, we create in the first row of the spreadsheet the header containing the columns Name and link .
'We create the header cells ws.Cells(1, 1) = "name" ws.Cells(1, 2) = "link"
Now before analyzing the script we need to understand the result of the API. If you open the link https://pokeapi.co/api/v2/pokemon in your browser you will see the following result:
{ "count": 964, "next": "https://pokeapi.co/api/v2/pokemon?offset=20&limit=20", "previous": null, "results": [ { "name": "bulbasaur", "url": "https://pokeapi.co/api/v2/pokemon/1/" }, { "name": "ivysaur", "url": "https://pokeapi.co/api/v2/pokemon/2/" }, { "name": "venusaur", "url": "https://pokeapi.co/api/v2/pokemon/3/" }, { "name": "charmander", "url": "https://pokeapi.co/api/v2/pokemon/4/" }, { "name": "charmeleon", "url": "https://pokeapi.co/api/v2/pokemon/5/" }, { "name": "charizard", "url": "https://pokeapi.co/api/v2/pokemon/6/" }, { "name": "squirtle", "url": "https://pokeapi.co/api/v2/pokemon/7/" }, { "name": "wartortle", "url": "https://pokeapi.co/api/v2/pokemon/8/" }, { "name": "blastoise", "url": "https://pokeapi.co/api/v2/pokemon/9/" }, { "name": "caterpie", "url": "https://pokeapi.co/api/v2/pokemon/10/" }, { "name": "metapod", "url": "https://pokeapi.co/api/v2/pokemon/11/" }, { "name": "butterfree", "url": "https://pokeapi.co/api/v2/pokemon/12/" }, { "name": "weedle", "url": "https://pokeapi.co/api/v2/pokemon/13/" }, { "name": "kakuna", "url": "https://pokeapi.co/api/v2/pokemon/14/" }, { "name": "beedrill", "url": "https://pokeapi.co/api/v2/pokemon/15/" }, { "name": "pidgey", "url": "https://pokeapi.co/api/v2/pokemon/16/" }, { "name": "pidgeotto", "url": "https://pokeapi.co/api/v2/pokemon/17/" }, { "name": "pidgeot", "url": "https://pokeapi.co/api/v2/pokemon/18/" }, { "name": "rattata", "url": "https://pokeapi.co/api/v2/pokemon/19/" }, { "name": "raticate", "url": "https://pokeapi.co/api/v2/pokemon/20/" } ] }
We are interested in the property. results
, an array containing a list of pokemons with their names and links to more details. We will access this matrix at jsonobject("results")
and we will loop to display each pokemon result in a new row of our table.
'We loop the results property of the API response i = 2 'We will start the counter on line 2 For Each pokemon InJsonObject("results") ws.Cells(i, 1) = pokemon("name") ws.Cells(i, 2) = pokemon("url") i = i + 1 next
If everything goes as expected, by pressing f5
to run our macro, in your spreadsheet you should see the following result:
Conclusion
This was a very simple example of a query, with the object of HT TP it is possible to perform all types of requests, GET, POST, UPDATE, … The interesting thing is to understand how the request is made and how you can display the result, thanks to the VBA JSON library, which already drastically reduces the work required. Now you just need to adapt this flow and script to your needs.
3.7
6
votos
Nota do Artigo
This tutorial is for you if you’re an Excel user who would like to learn how to extend your spreadsheets with data from APIs. You might be one of the people who spend most of their workdays in Excel and do calculations and visualizations but still work manually to import or even just copy and paste data from external sources.
If data is available from an API, however, there are various ways in which can consume it directly from Excel, saving you time and preventing errors.
That is, unfortunately, just as long you are using a Windows PC. The integrations presented in this article do not work in Excel for Mac or the web-based version of the Office apps.
BASF provides the Periodic Table of Elements API, which enables you to query the periodic table and retrieve all kinds of information about chemical elements.
We assume you came here from the Introduction to APIs. If not, please check out that document to learn the basics of APIs before continuing.
The Periodic Table of Elements API follows the CRUD/RESTful paradigm but implements only a subset. Specifically, the periodic table is read-only and allows HTTP GET requests exclusively. It is also limited to one resource called «element».
The base URL for the API is https://dev.api.basf.com/chemical/. Every endpoint starts with this URL.
For the elements, there are two endpoints. The first is a collection endpoint that allows you to either retrieve the full periodic table or request a subset of elements through filter parameters. It is called a collection endpoint because it always returns a collection data structure containing multiple items. The second is an individual resource endpoint that allows you to retrieve a single element.
Both endpoints return JSON-formatted responses.
All BASF APIs require authentication. The Periodic Table of Elements API uses an app-only context with an API key. If you haven’t done so, please read the introduction, terminology, and app-only context sections of the Authentication and Authorization guide. You can skip the part on the app and user context and OAuth as it is not required for this API.
To use authentication, you need to create an account and an application. The process gives you a Client ID, which you can then use as your API key. If you do not have an API key, follow the steps outlined in the Get Started guide. Make sure you select the API product BASF Periodic Table while creating your app.
In Excel for Windows, there are three options for getting external data from an API, which we’ll investigate throughout this article.
The first option is the built-in WEBSERVICE() function. Retrieving data from a URL is a single step. However, extracting the information is rather cumbersome as Excel doesn’t understand JSON responses and you have to use string processing functions. Also, as the function only supports the URL as a parameter, this works only with APIs that allow you to provide authentication information as part of the URL. The BASF Periodic Table of Elements API fits the criteria, as it uses a query string for authentication. For other APIs that require custom headers, though, you’re out of luck.
The second option is using VBA, Visual Basic for Applications, to write a Macro in Basic code to consume an API and fill the sheet programmatically. It is the most powerful and flexible option and should work with all kinds of APIs, but it requires you to have some basic programming knowledge.
The third option is the Power Query builder that allows you to gather and combine data from different sources, including APIs. If you have already used the Power Query builder, for example, to access a database directly, this is the sensible way to do it. It’s also a good choice if you do not want to work with code and the WEBSERVICE() option is too limited.
We’ll use an individual resource endpoint with the WEBSERVICE() function as well as the VBA macro, and we’ll demonstrate the collection endpoint with the Power Query builder.
Before we introduce the WEBSERVICE() function, let’s look at functions in Excel in general. They allow you to fill a table cell with computed data. To enter a function into a cell, you start with the equals sign (=), followed by the function name, a left parenthesis [(], your arguments (if needed), and a right parenthesis [)] to end. If there is more than one argument, you separate them with semicolons (;). A common function that you may have used is SUM(), which adds the data from a number of other cells and returns the result. It takes a range of cells as its argument.
The WEBSERVICE() function also takes one argument, a URL. Then, Excel issues an HTTP GET request to fetch whatever is available at that URL, and puts it into the cell as its result. If we want to do an API requests, since the function only allows a single argument, we have to encode the full API request into the URL. As mentioned before, this rules out APIs that require other HTTP methods (such as POST) or custom headers, e.g., for authorization. With the Periodic Table API, however, we put the API key in the URL and only need the GET method.
Open a new Excel workbook, and enter the following line into a cell, replacing the string APIKEY with the previously generated key:
=WEBSERVICE("https://dev.api.basf.com/chemical/elements/He?x-api-key=APIKEY")
Once you hit Enter or click on another cell, you should see a JSON structure describing the Helium element appear in your cell.
As mentioned before, every API endpoint starts with the base URL. For the Periodic Table API, that is https://dev.api.basf.com/chemical/. Then, the path to a resource or collection endpoint follows. In our example, we retrieved a single element, hence, we used a resource endpoint. Typically, the path to a resource starts with the pluralized resource name, followed by a slash (/) and finally an identifier. If you look at the URL above, you can identify elements/He as this path. The question mark (?) separates the parameter section, and you can see the x-api-key parameter that authenticates the request.
We can separate the base URL and the API key from the URL and build the URL dynamically through string concatenation. That is useful if you have multiple requests in the same Excel sheet and, let’s say, you want to share this sheet with another person who then provides a different API key. The other person then only has to enter their API key in one designated cell instead of modifying your API requests.
As next step, we’ll create a sheet that allows users to modify the base URL and API key in one place. We’ll also apply string processing functions to parse a single field from the JSON response. You can follow along these steps. If something is unclear or doesn’t work for you, you can compare your sheet with the screenshots as well as the full Excel workbook that we’ll provide as download further below.
Here we go:
- Type «Base URL» in A1 and the base URL https://dev.api.basf.com/chemical/ in B1.
- Type «API Key» in A2 and paste your API key from the developer portal in B2.
- Type «Element» as a header in A4 and some elements below it. As an example, we’ll put «H» in A5 and «N» in A6.
- Type «API URL» as a header in B4. To build the URL, concatenate the following elements: a reference to the field containing the base URL, «elements/», a reference to the field containing the element name, «?x-api-key=», and a reference to the field containing the API key. You need to use absolute references to base URL and API key and a relative reference to the element name. Your function call should look like this: =CONCAT($B$1;»elements/»;A5;»?x-api-key=»;$B$2)
- Type «API Response» as a header in C4. Make the webservice call using the URL you built in the previous step by typing =WEBSERVICE(B5) in C5. You should see a JSON API response appear in the cell.
- Type «Atomic Mass» as a header in D4. As there is no JSON parser in Excel, we copy a substring from the API response between the atomic_mass field and the subsequent field, covalent_radius. The whole function looks like this: =MID(C5;SEARCH(«atomic_mass»;C5)+13;SEARCH(«covalent_radius»;C5)-SEARCH(«atomic_mass»;C5)-16) You should now see only the atomic mass as a number in the cell.
- Select the cells B5 to D5, grab the right bottom corner, and drag them down to the B6 to D6. You should see the API URL, API response and atomic mass for the second element appear.
- To get the atomic mass for more elements, add a new element symbol in column A and copy or drag down the three other columns.
Please note that Excel function names are localized along with the Excel user interface. If your Excel installation is in German, replace the names based on this list:
- CONCAT – TEXTKETTE
- MID – TEIL
- SEARCH – SUCHEN
You can get a working Excel workbook in the PeriodicTableWebservice.xlsx file (TODO: add download).
Visual Basic for Applications, or VBA for short, is a powerful programming language that you can use to automate Microsoft Office applications, including Excel. VBA is closely related to, but not fully compatible with, other versions of the Basic language (such as Visual Basic .NET).
If you not worked with VBA before but have programming skills in a different language, you have to learn a new syntax, but should be able to understand the structure of the macro we build in this section.
Our goal is to retrieve various attributes of specific chemical elements (but not the entire periodic table). The sheet layout uses one row per element. The user should enter the element names into the first column of the sheet (A). Then, upon running the macro, it should go through the list of rows and fill the other columns with details. For example, the atomic mass in the second column (B) and the covalent radius in the third column (C).
Open Excel with a new, empty sheet. Then, press the combination Alt and F11 on your keyboard to launch the Visual Basic editor.
On the left side of the editor, you can see the project structure. There’s an entry for your sheet. Double-click on this entry to open the code view where you can enter Basic code that is specific to that sheet.
We start by creating two global string variables, one for the base URL and the API key. It helps us separate them from the code itself so we can swap them if necessary.
Dim baseUrl, apiKey As String
It’s not possible to assign a value to these variables yet. We’ll do that later.
As mentioned under the objective above, we need to retrieve specific elements that the user enters into the sheet, not the full periodic table. For this purpose, we use the API endpoint for specific elements and have to make multiple requests. Hence it is useful to develop an abstraction and put the logic for the API request into a function.
In VBA, you can define functions with the Function keyword. They have parameters with types and a return type. Here is the definition of our getElement() function:
Function getElement(id As String) As Object
Next, we create a script control. That is actually a workaround because there is no native JSON parser available and we don’t want to install a third-party module. The script control can execute JScript, which is a version of JavaScript, and thus understand JSON.
Dim scriptControl As Object Set scriptControl = CreateObject("MSScriptControl.ScriptControl") scriptControl.Language = "JScript"
Then, we can make an HTTP request to the API endpoint. VBA makes HTTP requests through the XMLHTTP object. While building the request, we use string concatenation to build the API URL and include the base URL and API key from the variables we defined earlier.
With CreateObject("MSXML2.XMLHTTP") .Open "GET", baseUrl + "elements/" + id + "?x-api-key=" + apiKey, False .send
Once we have a response, we can parse it with the script control and assign the result to the function name, which is VBA’s way of specifying the returned value for a function:
Set getElement = scriptControl.Eval("(" + .responsetext + ")") .abort
After that, all is left is closing the With-block and the function. The VBA editor might have already generated this code for you automatically.
End With End Function
Macros are public VBA subroutines. Let’s call ours getPeriodicElementData():
Public Sub getPeriodicElementData()
Inside the subroutine, we first assign some values to our global variables. Add these lines and replace APIKEY with your API key from the developer portal:
baseUrl = [https://dev.api.basf.com/chemical/](https://dev.api.basf.com/chemical/) apiKey = "APIKEY"
Then, as we want to go through our sheet row by row to get element data, we set up a counter variable:
Dim row As Integer row = 1
Our algorithm goes through all rows until it stops at an empty row. We can achieve this with a Do-Until-loop:
Do Until IsEmpty(Cells(row, 1))
Inside the loop, we call our getElement() function we created in the previous section. By using a With-block, we can access the attributes of the elements directly and write them in the cells. Our sample code reads two attributes, atomic mass and covalent radius, which go in the row of the current element and the second or third column respectively.
With getElement(Cells(row, 1)) Cells(row, 2) = .atomic_mass Cells(row, 3) = .covalent_radius End With
If you want, you can extend this code with more properties from the element object and put them in additional columns. Tip: Use the WEBSERVICE() function to get the full JSON structure so you can see the available properties.
Afterwards, make sure to increase the row variable, otherwise you have an infinite loop:
row = row + 1
Finally, we close the loop and the subroutine. The VBA editor might have already generated this code for you automatically.
Loop End Sub
Before we can run the macro, we should add a couple of element symbols in the first column (A) so that the macro has something to do. As an example, use the elements H, S and O. Use one row for each, so that H is in the first, S in the second and O in the third row.
During development, you can run your macro directly from the VBA editor, using either the play button in the toolbar, the Run menu, or the F5 key on your keyboard.
The macro should run and automatically fill the other columns for your three rows.
To re-run your macro later when you’ve closed the VBA editor, follow these steps:
- Open the View ribbon.
- Click on Macros.
- Select the macro and click Run to execute.
The Power Query builder provides access to a variety of data sources. It even enables the combination of data from multiple sources. In this tutorial, though, we’ll just show you the basics to make an API call and display the results in your Excel sheet. We use the collection endpoint to retrieve the complete periodic table.
To get started, go to the Data ribbon, click the Get Data button, choose From Other Sources, and From Web.
In the popup window, enter the URL for the API. You can configure advanced settings such as HTTP headers for your request, but the BASF Periodic Table API doesn’t need any. You have to provide the full URL to the collection endpoint and add the query parameter for authentication directly into the URL. In the following sample, replace APIKEY with the Client ID of your application
https://dev.api.basf.com/chemical/elements?x-api-key=APIKEY
Once you confirm with OK, the Power Query Builder opens and shows the API response. At first, you just see the container element items. Right-click on it and select Drill Down.
Note the list of applied steps on the right side of the window. It shows you everything you did in the query builder and you can undo any step by clicking the X next to it.
Now you see a list of records and you can click on each to see the different attributes of each element. The next step is converting this list into a table and you do this by clicking the To Table button.
A popup with additional options may appear which you can simply confirm with OK. After that, you see the list as a table but it still has just one column that we need to expand into multiple columns so you can see each attribute of the element in its own column. To do so, click the expand icon (<>) in the column header.
In the popup, select the fields that you like or keep all fields intact. You can uncheck «Use original column name as prefix» as the prefix doesn’t provide any value when you’re just working with one data source.
Once you see the data you want in the query builder, your final step is transferring the table into the Excel sheet. You do this with the Close&Load button.
You’re done! Your excel sheet contains the periodic table of elements now.
Congratulations on making it through the tutorial! We hope it deepened your understanding of APIs and how to consume them with Excel. Feel free to contact developer@basf.com with feedback and questions.
Вступление
API означает интерфейс прикладного программирования
API для VBA подразумевает набор методов, которые позволяют прямое взаимодействие с операционной системой
Системные вызовы могут выполняться путем выполнения процедур, определенных в файлах DLL
замечания
Общие файлы библиотеки операционной среды (DLL):
Динамическая библиотека ссылок | Описание |
---|---|
Advapi32.dll | Библиотека дополнительных сервисов для API, включая многие вызовы безопасности и реестра |
cOMDLG32.DLL | Общая библиотека API диалога |
Gdi32.dll | Интерфейс API интерфейса графического интерфейса |
Kernel32.dll | Поддержка 32-битного базового API ядра Windows |
Lz32.dll | 32-разрядные процедуры сжатия |
Mpr.dll | Многоуровневая библиотека маршрутизаторов |
Netapi32.dll | 32-битная библиотека сетевого API |
Shell32.dll | 32-разрядная библиотека API оболочки |
User32.dll | Библиотека для пользовательских интерфейсов |
Version.dll | Библиотека версий |
Winmm.dll | Мультимедийная библиотека Windows |
WINSPOOL.DRV | Интерфейс диспетчера очереди печати, содержащий вызовы API очереди печати |
Новые аргументы, используемые для системы 64:
Тип | Вещь | Описание |
---|---|---|
спецификатор | PtrSafe | Указывает, что оператор Declare совместим с 64-битными. Этот атрибут является обязательным для 64-битных систем |
Тип данных | LongPtr | Переменный тип данных, который представляет собой 4-байтовый тип данных в 32-разрядных версиях и 8-байтовый тип данных в 64-разрядных версиях Office 2010. Это рекомендуемый способ объявления указателя или дескриптора для нового кода, но также для устаревшего кода, если он должен запускаться в 64-разрядной версии Office 2010. Он поддерживается только в среде исполнения VBA 7 на 32-разрядной и 64-разрядной версиях. Обратите внимание, что вы можете присвоить ему числовые значения, но не числовые типы |
Тип данных | Долго долго | Это 8-байтовый тип данных, доступный только в 64-разрядных версиях Office 2010. Вы можете назначать числовые значения, но не числовые типы (чтобы избежать усечения) |
преобразование | оператор | CLngPtr Преобразует простое выражение в тип данных LongPtr |
преобразование | оператор | CLngLng Преобразует простое выражение в тип данных LongLong |
функция | VarPtr | Конвертер вариантов. Возвращает LongPtr в 64-разрядных версиях, а длинный 32-разрядный (4 байта) |
функция | ObjPtr | Конвертер объектов. Возвращает LongPtr в 64-разрядных версиях, а длинный 32-разрядный (4 байта) |
функция | StrPtr | Преобразователь строк. Возвращает LongPtr в 64-разрядных версиях, а длинный 32-разрядный (4 байта) |
Полная ссылка на сигнатуры вызовов:
-
Win32api32.txt для Visual Basic 5.0 (старые декларации API, последний раз рассмотренный в марте 2005 г., Microsoft)
-
Win32API_PtrSafe с 64-разрядной поддержкой (Office 2010, Microsoft)
Объявление процедуры DLL для работы с различными версиями VBA:
Option Explicit
#If Win64 Then
Private Declare PtrSafe Sub xLib "Kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
#ElseIf Win32 Then
Private Declare Sub apiSleep Lib "Kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
#End If
Вышеприведенное объявление указывает VBA, как вызвать функцию «Сон», определенную в файле Kernel32.dll
Win64 и Win32 — это предопределенные константы, используемые для условной компиляции
Предварительно определенные константы
Некоторые константы компиляции уже заранее определены. Какие из них будут зависеть от битности офисной версии, в которой вы используете VBA. Обратите внимание, что Vba7 был представлен вместе с Office 2010 для поддержки 64-разрядных версий Office.
постоянная | 16 бит | 32-битный | 64-битный |
---|---|---|---|
VBA6 | Ложь | Если Vba6 | Ложь |
Vba7 | Ложь | Если Vba7 | Правда |
Win16 | Правда | Ложь | Ложь |
Win32 | Ложь | Правда | Правда |
Win64 | Ложь | Ложь | Правда |
макинтош | Ложь | Если Mac | Если Mac |
Эти константы относятся к версии Office, а не к версии Windows. Например, Win32 = TRUE в 32-разрядном Office, даже если ОС — это 64-разрядная версия Windows.
Основное отличие при объявлении API-интерфейсов — от 32-битных до 64-битных версий Office, которые ввели новые типы параметров (подробнее см. Раздел «Примечания»)
Заметки:
- Объявления размещаются в верхней части модуля и вне любых подписок или функций
- Процедуры, объявленные в стандартных модулях, общедоступны по умолчанию
- Чтобы объявить процедуру, закрытую для модуля, перед объявлением ключевым словом
Private
- Процедуры DLL, объявленные в любом другом типе модуля, являются приватными для этого модуля
Простой пример для вызова API сна:
Public Sub TestPause()
Dim start As Double
start = Timer
Sleep 9000 'Pause execution for 9 seconds
Debug.Print "Paused for " & Format(Timer - start, "#,###.000") & " seconds"
'Immediate window result: Paused for 9.000 seconds
End Sub
Рекомендуется создать выделенный модуль API для обеспечения легкого доступа к системным функциям из оберток VBA — обычных VBA Subs или функций, которые инкапсулируют детали, необходимые для фактического системного вызова, такие как параметры, используемые в библиотеках, и инициализацию этих параметров
Модуль может содержать все объявления и зависимости:
- Подписи методов и требуемые структуры данных
- Обертки, которые выполняют проверку ввода, и обеспечивают, чтобы все параметры передавались как ожидалось
Чтобы объявить процедуру DLL, добавьте оператор Declare
в раздел Declarations окна кода.
Если процедура возвращает значение, объявите ее как функцию :
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]]...])]
- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!
Также следует отметить, что большинство недействительных вызовов API приведет к сбою Excel и, возможно, к поврежденным файлам данных
- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!
Office 2011 для Mac
Private Declare Function system Lib "libc.dylib" (ByVal command As String) As Long
Sub RunSafari()
Dim result As Long
result = system("open -a Safari --args http://www.google.com")
Debug.Print Str(result)
End Sub
В приведенных ниже примерах (Windows API — выделенный модуль (1 и 2)) показан модуль API, который включает в себя общие объявления для Win64 и Win32
Windows API — выделенный модуль (1 из 2)
Option Explicit
#If Win64 Then 'Win64 = True, Win32 = False, Win16 = False
Private Declare PtrSafe Sub apiCopyMemory Lib "Kernel32" Alias "RtlMoveMemory" (MyDest As Any, MySource As Any, ByVal MySize As Long)
Private Declare PtrSafe Sub apiExitProcess Lib "Kernel32" Alias "ExitProcess" (ByVal uExitCode As Long)
Private Declare PtrSafe Sub apiSetCursorPos Lib "User32" Alias "SetCursorPos" (ByVal X As Integer, ByVal Y As Integer)
Private Declare PtrSafe Sub apiSleep Lib "Kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
Private Declare PtrSafe Function apiAttachThreadInput Lib "User32" Alias "AttachThreadInput" (ByVal idAttach As Long, ByVal idAttachTo As Long, ByVal fAttach As Long) As Long
Private Declare PtrSafe Function apiBringWindowToTop Lib "User32" Alias "BringWindowToTop" (ByVal lngHWnd As Long) As Long
Private Declare PtrSafe Function apiCloseWindow Lib "User32" Alias "CloseWindow" (ByVal hWnd As Long) As Long
Private Declare PtrSafe Function apiDestroyWindow Lib "User32" Alias "DestroyWindow" (ByVal hWnd As Long) As Boolean
Private Declare PtrSafe Function apiEndDialog Lib "User32" Alias "EndDialog" (ByVal hWnd As Long, ByVal result As Long) As Boolean
Private Declare PtrSafe Function apiEnumChildWindows Lib "User32" Alias "EnumChildWindows" (ByVal hWndParent As Long, ByVal pEnumProc As Long, ByVal lParam As Long) As Long
Private Declare PtrSafe Function apiExitWindowsEx Lib "User32" Alias "ExitWindowsEx" (ByVal uFlags As Long, ByVal dwReserved As Long) As Long
Private Declare PtrSafe Function apiFindExecutable Lib "Shell32" Alias "FindExecutableA" (ByVal lpFile As String, ByVallpDirectory As String, ByVal lpResult As String) As Long
Private Declare PtrSafe Function apiFindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare PtrSafe Function apiFindWindowEx Lib "User32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Private Declare PtrSafe Function apiGetActiveWindow Lib "User32" Alias "GetActiveWindow" () As Long
Private Declare PtrSafe Function apiGetClassNameA Lib "User32" Alias "GetClassNameA" (ByVal hWnd As Long, ByVal szClassName As String, ByVal lLength As Long) As Long
Private Declare PtrSafe Function apiGetCommandLine Lib "Kernel32" Alias "GetCommandLineW" () As Long
Private Declare PtrSafe Function apiGetCommandLineParams Lib "Kernel32" Alias "GetCommandLineA" () As Long
Private Declare PtrSafe Function apiGetDiskFreeSpaceEx Lib "Kernel32" Alias "GetDiskFreeSpaceExA" (ByVal lpDirectoryName As String, lpFreeBytesAvailableToCaller As Currency, lpTotalNumberOfBytes As Currency, lpTotalNumberOfFreeBytes As Currency) As Long
Private Declare PtrSafe Function apiGetDriveType Lib "Kernel32" Alias "GetDriveTypeA" (ByVal nDrive As String) As Long
Private Declare PtrSafe Function apiGetExitCodeProcess Lib "Kernel32" Alias "GetExitCodeProcess" (ByVal hProcess As Long, lpExitCode As Long) As Long
Private Declare PtrSafe Function apiGetForegroundWindow Lib "User32" Alias "GetForegroundWindow" () As Long
Private Declare PtrSafe Function apiGetFrequency Lib "Kernel32" Alias "QueryPerformanceFrequency" (cyFrequency As Currency) As Long
Private Declare PtrSafe Function apiGetLastError Lib "Kernel32" Alias "GetLastError" () As Integer
Private Declare PtrSafe Function apiGetParent Lib "User32" Alias "GetParent" (ByVal hWnd As Long) As Long
Private Declare PtrSafe Function apiGetSystemMetrics Lib "User32" Alias "GetSystemMetrics" (ByVal nIndex As Long) As Long
Private Declare PtrSafe Function apiGetSystemMetrics32 Lib "User32" Alias "GetSystemMetrics" (ByVal nIndex As Long) As Long
Private Declare PtrSafe Function apiGetTickCount Lib "Kernel32" Alias "QueryPerformanceCounter" (cyTickCount As Currency) As Long
Private Declare PtrSafe Function apiGetTickCountMs Lib "Kernel32" Alias "GetTickCount" () As Long
Private Declare PtrSafe Function apiGetUserName Lib "AdvApi32" Alias "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long
Private Declare PtrSafe Function apiGetWindow Lib "User32" Alias "GetWindow" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
Private Declare PtrSafe Function apiGetWindowRect Lib "User32" Alias "GetWindowRect" (ByVal hWnd As Long, lpRect As winRect) As Long
Private Declare PtrSafe Function apiGetWindowText Lib "User32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal szWindowText As String, ByVal lLength As Long) As Long
Private Declare PtrSafe Function apiGetWindowThreadProcessId Lib "User32" Alias "GetWindowThreadProcessId" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
Private Declare PtrSafe Function apiIsCharAlphaNumericA Lib "User32" Alias "IsCharAlphaNumericA" (ByVal byChar As Byte) As Long
Private Declare PtrSafe Function apiIsIconic Lib "User32" Alias "IsIconic" (ByVal hWnd As Long) As Long
Private Declare PtrSafe Function apiIsWindowVisible Lib "User32" Alias "IsWindowVisible" (ByVal hWnd As Long) As Long
Private Declare PtrSafe Function apiIsZoomed Lib "User32" Alias "IsZoomed" (ByVal hWnd As Long) As Long
Private Declare PtrSafe Function apiLStrCpynA Lib "Kernel32" Alias "lstrcpynA" (ByVal pDestination As String, ByVal pSource As Long, ByVal iMaxLength As Integer) As Long
Private Declare PtrSafe Function apiMessageBox Lib "User32" Alias "MessageBoxA" (ByVal hWnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Long) As Long
Private Declare PtrSafe Function apiOpenIcon Lib "User32" Alias "OpenIcon" (ByVal hWnd As Long) As Long
Private Declare PtrSafe Function apiOpenProcess Lib "Kernel32" Alias "OpenProcess" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare PtrSafe Function apiPathAddBackslashByPointer Lib "ShlwApi" Alias "PathAddBackslashW" (ByVal lpszPath As Long) As Long
Private Declare PtrSafe Function apiPathAddBackslashByString Lib "ShlwApi" Alias "PathAddBackslashW" (ByVal lpszPath As String) As Long 'http://msdn.microsoft.com/en-us/library/aa155716%28office.10%29.aspx
Private Declare PtrSafe Function apiPostMessage Lib "User32" Alias "PostMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare PtrSafe Function apiRegQueryValue Lib "AdvApi32" Alias "RegQueryValue" (ByVal hKey As Long, ByVal sValueName As String, ByVal dwReserved As Long, ByRef lValueType As Long, ByVal sValue As String, ByRef lResultLen As Long) As Long
Private Declare PtrSafe Function apiSendMessage Lib "User32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare PtrSafe Function apiSetActiveWindow Lib "User32" Alias "SetActiveWindow" (ByVal hWnd As Long) As Long
Private Declare PtrSafe Function apiSetCurrentDirectoryA Lib "Kernel32" Alias "SetCurrentDirectoryA" (ByVal lpPathName As String) As Long
Private Declare PtrSafe Function apiSetFocus Lib "User32" Alias "SetFocus" (ByVal hWnd As Long) As Long
Private Declare PtrSafe Function apiSetForegroundWindow Lib "User32" Alias "SetForegroundWindow" (ByVal hWnd As Long) As Long
Private Declare PtrSafe Function apiSetLocalTime Lib "Kernel32" Alias "SetLocalTime" (lpSystem As SystemTime) As Long
Private Declare PtrSafe Function apiSetWindowPlacement Lib "User32" Alias "SetWindowPlacement" (ByVal hWnd As Long, ByRef lpwndpl As winPlacement) As Long
Private Declare PtrSafe Function apiSetWindowPos Lib "User32" Alias "SetWindowPos" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Declare PtrSafe Function apiSetWindowText Lib "User32" Alias "SetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String) As Long
Private Declare PtrSafe Function apiShellExecute Lib "Shell32" Alias "ShellExecuteA" (ByVal hWnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
Private Declare PtrSafe Function apiShowWindow Lib "User32" Alias "ShowWindow" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
Private Declare PtrSafe Function apiShowWindowAsync Lib "User32" Alias "ShowWindowAsync" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
Private Declare PtrSafe Function apiStrCpy Lib "Kernel32" Alias "lstrcpynA" (ByVal pDestination As String, ByVal pSource As String, ByVal iMaxLength As Integer) As Long
Private Declare PtrSafe Function apiStringLen Lib "Kernel32" Alias "lstrlenW" (ByVal lpString As Long) As Long
Private Declare PtrSafe Function apiStrTrimW Lib "ShlwApi" Alias "StrTrimW" () As Boolean
Private Declare PtrSafe Function apiTerminateProcess Lib "Kernel32" Alias "TerminateProcess" (ByVal hWnd As Long, ByVal uExitCode As Long) As Long
Private Declare PtrSafe Function apiTimeGetTime Lib "Winmm" Alias "timeGetTime" () As Long
Private Declare PtrSafe Function apiVarPtrArray Lib "MsVbVm50" Alias "VarPtr" (Var() As Any) As Long
Private Type browseInfo 'used by apiBrowseForFolder
hOwner As Long
pidlRoot As Long
pszDisplayName As String
lpszTitle As String
ulFlags As Long
lpfn As Long
lParam As Long
iImage As Long
End Type
Private Declare PtrSafe Function apiBrowseForFolder Lib "Shell32" Alias "SHBrowseForFolderA" (lpBrowseInfo As browseInfo) As Long
Private Type CHOOSECOLOR 'used by apiChooseColor; http://support.microsoft.com/kb/153929 and http://www.cpearson.com/Excel/Colors.aspx
lStructSize As Long
hWndOwner As Long
hInstance As Long
rgbResult As Long
lpCustColors As String
flags As Long
lCustData As Long
lpfnHook As Long
lpTemplateName As String
End Type
Private Declare PtrSafe Function apiChooseColor Lib "ComDlg32" Alias "ChooseColorA" (pChoosecolor As CHOOSECOLOR) As Long
Private Type FindWindowParameters 'Custom structure for passing in the parameters in/out of the hook enumeration function; could use global variables instead, but this is nicer
strTitle As String 'INPUT
hWnd As Long 'OUTPUT
End Type 'Find a specific window with dynamic caption from a list of all open windows: http://www.everythingaccess.com/tutorials.asp?ID=Bring-an-external-application-window-to-the-foreground
Private Declare PtrSafe Function apiEnumWindows Lib "User32" Alias "EnumWindows" (ByVal lpEnumFunc As LongPtr, ByVal lParam As LongPtr) As Long
Private Type lastInputInfo 'used by apiGetLastInputInfo, getLastInputTime
cbSize As Long
dwTime As Long
End Type
Private Declare PtrSafe Function apiGetLastInputInfo Lib "User32" Alias "GetLastInputInfo" (ByRef plii As lastInputInfo) As Long
'http://www.pgacon.com/visualbasic.htm#Take%20Advantage%20of%20Conditional%20Compilation
'Logical and Bitwise Operators in Visual Basic: http://msdn.microsoft.com/en-us/library/wz3k228a(v=vs.80).aspx and http://stackoverflow.com/questions/1070863/hidden-features-of-vba
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
Private Declare PtrSafe Sub apiGetLocalTime Lib "Kernel32" Alias "GetLocalTime" (lpSystem As SystemTime)
Private Type pointAPI 'used by apiSetWindowPlacement
X As Long
Y As Long
End Type
Private Type rectAPI 'used by apiSetWindowPlacement
Left_Renamed As Long
Top_Renamed As Long
Right_Renamed As Long
Bottom_Renamed As Long
End Type
Private Type winPlacement 'used by apiSetWindowPlacement
length As Long
flags As Long
showCmd As Long
ptMinPosition As pointAPI
ptMaxPosition As pointAPI
rcNormalPosition As rectAPI
End Type
Private Declare PtrSafe Function apiGetWindowPlacement Lib "User32" Alias "GetWindowPlacement" (ByVal hWnd As Long, ByRef lpwndpl As winPlacement) As Long
Private Type winRect 'used by apiMoveWindow
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare PtrSafe Function apiMoveWindow Lib "User32" Alias "MoveWindow" (ByVal hWnd As Long, xLeft As Long, ByVal yTop As Long, wWidth As Long, ByVal hHeight As Long, ByVal repaint As Long) As Long
Private Declare PtrSafe Function apiInternetOpen Lib "WiniNet" Alias "InternetOpenA" (ByVal sAgent As String, ByVal lAccessType As Long, ByVal sProxyName As String, ByVal sProxyBypass As String, ByVal lFlags As Long) As Long 'Open the Internet object 'ex: lngINet = InternetOpen(“MyFTP Control”, 1, vbNullString, vbNullString, 0)
Private Declare PtrSafe Function apiInternetConnect Lib "WiniNet" Alias "InternetConnectA" (ByVal hInternetSession As Long, ByVal sServerName As String, ByVal nServerPort As Integer, ByVal sUsername As String, ByVal sPassword As String, ByVal lService As Long, ByVal lFlags As Long, ByVal lContext As Long) As Long 'Connect to the network 'ex: lngINetConn = InternetConnect(lngINet, "ftp.microsoft.com", 0, "anonymous", "wall[email protected]", 1, 0, 0)
Private Declare PtrSafe Function apiFtpGetFile Lib "WiniNet" Alias "FtpGetFileA" (ByVal hFtpSession As Long, ByVal lpszRemoteFile As String, ByVal lpszNewFile As String, ByVal fFailIfExists As Boolean, ByVal dwFlagsAndAttributes As Long, ByVal dwFlags As Long, ByVal dwContext As Long) As Boolean 'Get a file 'ex: blnRC = FtpGetFile(lngINetConn, "dirmap.txt", "c:dirmap.txt", 0, 0, 1, 0)
Private Declare PtrSafe Function apiFtpPutFile Lib "WiniNet" Alias "FtpPutFileA" (ByVal hFtpSession As Long, ByVal lpszLocalFile As String, ByVal lpszRemoteFile As String, ByVal dwFlags As Long, ByVal dwContext As Long) As Boolean 'Send a file 'ex: blnRC = FtpPutFile(lngINetConn, “c:dirmap.txt”, “dirmap.txt”, 1, 0)
Private Declare PtrSafe Function apiFtpDeleteFile Lib "WiniNet" Alias "FtpDeleteFileA" (ByVal hFtpSession As Long, ByVal lpszFileName As String) As Boolean 'Delete a file 'ex: blnRC = FtpDeleteFile(lngINetConn, “test.txt”)
Private Declare PtrSafe Function apiInternetCloseHandle Lib "WiniNet" (ByVal hInet As Long) As Integer 'Close the Internet object 'ex: InternetCloseHandle lngINetConn 'ex: InternetCloseHandle lngINet
Private Declare PtrSafe Function apiFtpFindFirstFile Lib "WiniNet" Alias "FtpFindFirstFileA" (ByVal hFtpSession As Long, ByVal lpszSearchFile As String, lpFindFileData As WIN32_FIND_DATA, ByVal dwFlags As Long, ByVal dwContent As Long) As Long
Private Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Private Type WIN32_FIND_DATA
dwFileAttributes As Long
ftCreationTime As FILETIME
ftLastAccessTime As FILETIME
ftLastWriteTime As FILETIME
nFileSizeHigh As Long
nFileSizeLow As Long
dwReserved0 As Long
dwReserved1 As Long
cFileName As String * 1 'MAX_FTP_PATH
cAlternate As String * 14
End Type 'ex: lngHINet = FtpFindFirstFile(lngINetConn, "*.*", pData, 0, 0)
Private Declare PtrSafe Function apiInternetFindNextFile Lib "WiniNet" Alias "InternetFindNextFileA" (ByVal hFind As Long, lpvFindData As WIN32_FIND_DATA) As Long 'ex: blnRC = InternetFindNextFile(lngHINet, pData)
#ElseIf Win32 Then 'Win32 = True, Win16 = False
(продолжение во втором примере)
Windows API — выделенный модуль (2 из 2)
#ElseIf Win32 Then 'Win32 = True, Win16 = False
Private Declare Sub apiCopyMemory Lib "Kernel32" Alias "RtlMoveMemory" (MyDest As Any, MySource As Any, ByVal MySize As Long)
Private Declare Sub apiExitProcess Lib "Kernel32" Alias "ExitProcess" (ByVal uExitCode As Long)
'Private Declare Sub apiGetStartupInfo Lib "Kernel32" Alias "GetStartupInfoA" (lpStartupInfo As STARTUPINFO)
Private Declare Sub apiSetCursorPos Lib "User32" Alias "SetCursorPos" (ByVal X As Integer, ByVal Y As Integer) 'Logical and Bitwise Operators in Visual Basic: http://msdn.microsoft.com/en-us/library/wz3k228a(v=vs.80).aspx and http://stackoverflow.com/questions/1070863/hidden-features-of-vba 'http://www.pgacon.com/visualbasic.htm#Take%20Advantage%20of%20Conditional%20Compilation
Private Declare Sub apiSleep Lib "Kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
Private Declare Function apiAttachThreadInput Lib "User32" Alias "AttachThreadInput" (ByVal idAttach As Long, ByVal idAttachTo As Long, ByVal fAttach As Long) As Long
Private Declare Function apiBringWindowToTop Lib "User32" Alias "BringWindowToTop" (ByVal lngHWnd As Long) As Long
Private Declare Function apiCloseHandle Lib "Kernel32" (ByVal hObject As Long) As Long
Private Declare Function apiCloseWindow Lib "User32" Alias "CloseWindow" (ByVal hWnd As Long) As Long
'Private Declare Function apiCreatePipe Lib "Kernel32" (phReadPipe As Long, phWritePipe As Long, lpPipeAttributes As SECURITY_ATTRIBUTES, ByVal nSize As Long) As Long
'Private Declare Function apiCreateProcess Lib "Kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As Long, ByVal lpCommandLine As String, lpProcessAttributes As Any, lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, ByVal lpCurrentDriectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function apiDestroyWindow Lib "User32" Alias "DestroyWindow" (ByVal hWnd As Long) As Boolean
Private Declare Function apiEndDialog Lib "User32" Alias "EndDialog" (ByVal hWnd As Long, ByVal result As Long) As Boolean
Private Declare Function apiEnumChildWindows Lib "User32" Alias "EnumChildWindows" (ByVal hWndParent As Long, ByVal pEnumProc As Long, ByVal lParam As Long) As Long
Private Declare Function apiExitWindowsEx Lib "User32" Alias "ExitWindowsEx" (ByVal uFlags As Long, ByVal dwReserved As Long) As Long
Private Declare Function apiFindExecutable Lib "Shell32" Alias "FindExecutableA" (ByVal lpFile As String, ByVallpDirectory As String, ByVal lpResult As String) As Long
Private Declare Function apiFindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function apiFindWindowEx Lib "User32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Private Declare Function apiGetActiveWindow Lib "User32" Alias "GetActiveWindow" () As Long
Private Declare Function apiGetClassNameA Lib "User32" Alias "GetClassNameA" (ByVal hWnd As Long, ByVal szClassName As String, ByVal lLength As Long) As Long
Private Declare Function apiGetCommandLine Lib "Kernel32" Alias "GetCommandLineW" () As Long
Private Declare Function apiGetCommandLineParams Lib "Kernel32" Alias "GetCommandLineA" () As Long
Private Declare Function apiGetDiskFreeSpaceEx Lib "Kernel32" Alias "GetDiskFreeSpaceExA" (ByVal lpDirectoryName As String, lpFreeBytesAvailableToCaller As Currency, lpTotalNumberOfBytes As Currency, lpTotalNumberOfFreeBytes As Currency) As Long
Private Declare Function apiGetDriveType Lib "Kernel32" Alias "GetDriveTypeA" (ByVal nDrive As String) As Long
Private Declare Function apiGetExitCodeProcess Lib "Kernel32" (ByVal hProcess As Long, lpExitCode As Long) As Long
Private Declare Function apiGetFileSize Lib "Kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long
Private Declare Function apiGetForegroundWindow Lib "User32" Alias "GetForegroundWindow" () As Long
Private Declare Function apiGetFrequency Lib "Kernel32" Alias "QueryPerformanceFrequency" (cyFrequency As Currency) As Long
Private Declare Function apiGetLastError Lib "Kernel32" Alias "GetLastError" () As Integer
Private Declare Function apiGetParent Lib "User32" Alias "GetParent" (ByVal hWnd As Long) As Long
Private Declare Function apiGetSystemMetrics Lib "User32" Alias "GetSystemMetrics" (ByVal nIndex As Long) As Long
Private Declare Function apiGetTickCount Lib "Kernel32" Alias "QueryPerformanceCounter" (cyTickCount As Currency) As Long
Private Declare Function apiGetTickCountMs Lib "Kernel32" Alias "GetTickCount" () As Long
Private Declare Function apiGetUserName Lib "AdvApi32" Alias "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long
Private Declare Function apiGetWindow Lib "User32" Alias "GetWindow" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
Private Declare Function apiGetWindowRect Lib "User32" Alias "GetWindowRect" (ByVal hWnd As Long, lpRect As winRect) As Long
Private Declare Function apiGetWindowText Lib "User32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal szWindowText As String, ByVal lLength As Long) As Long
Private Declare Function apiGetWindowThreadProcessId Lib "User32" Alias "GetWindowThreadProcessId" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
Private Declare Function apiIsCharAlphaNumericA Lib "User32" Alias "IsCharAlphaNumericA" (ByVal byChar As Byte) As Long
Private Declare Function apiIsIconic Lib "User32" Alias "IsIconic" (ByVal hWnd As Long) As Long
Private Declare Function apiIsWindowVisible Lib "User32" Alias "IsWindowVisible" (ByVal hWnd As Long) As Long
Private Declare Function apiIsZoomed Lib "User32" Alias "IsZoomed" (ByVal hWnd As Long) As Long
Private Declare Function apiLStrCpynA Lib "Kernel32" Alias "lstrcpynA" (ByVal pDestination As String, ByVal pSource As Long, ByVal iMaxLength As Integer) As Long
Private Declare Function apiMessageBox Lib "User32" Alias "MessageBoxA" (ByVal hWnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Long) As Long
Private Declare Function apiOpenIcon Lib "User32" Alias "OpenIcon" (ByVal hWnd As Long) As Long
Private Declare Function apiOpenProcess Lib "Kernel32" Alias "OpenProcess" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function apiPathAddBackslashByPointer Lib "ShlwApi" Alias "PathAddBackslashW" (ByVal lpszPath As Long) As Long
Private Declare Function apiPathAddBackslashByString Lib "ShlwApi" Alias "PathAddBackslashW" (ByVal lpszPath As String) As Long 'http://msdn.microsoft.com/en-us/library/aa155716%28office.10%29.aspx
Private Declare Function apiPostMessage Lib "User32" Alias "PostMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function apiReadFile Lib "Kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As Any) As Long
Private Declare Function apiRegQueryValue Lib "AdvApi32" Alias "RegQueryValue" (ByVal hKey As Long, ByVal sValueName As String, ByVal dwReserved As Long, ByRef lValueType As Long, ByVal sValue As String, ByRef lResultLen As Long) As Long
Private Declare Function apiSendMessage Lib "User32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function apiSetActiveWindow Lib "User32" Alias "SetActiveWindow" (ByVal hWnd As Long) As Long
Private Declare Function apiSetCurrentDirectoryA Lib "Kernel32" Alias "SetCurrentDirectoryA" (ByVal lpPathName As String) As Long
Private Declare Function apiSetFocus Lib "User32" Alias "SetFocus" (ByVal hWnd As Long) As Long
Private Declare Function apiSetForegroundWindow Lib "User32" Alias "SetForegroundWindow" (ByVal hWnd As Long) As Long
Private Declare Function apiSetLocalTime Lib "Kernel32" Alias "SetLocalTime" (lpSystem As SystemTime) As Long
Private Declare Function apiSetWindowPlacement Lib "User32" Alias "SetWindowPlacement" (ByVal hWnd As Long, ByRef lpwndpl As winPlacement) As Long
Private Declare Function apiSetWindowPos Lib "User32" Alias "SetWindowPos" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Declare Function apiSetWindowText Lib "User32" Alias "SetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String) As Long
Private Declare Function apiShellExecute Lib "Shell32" Alias "ShellExecuteA" (ByVal hWnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
Private Declare Function apiShowWindow Lib "User32" Alias "ShowWindow" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
Private Declare Function apiShowWindowAsync Lib "User32" Alias "ShowWindowAsync" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
Private Declare Function apiStrCpy Lib "Kernel32" Alias "lstrcpynA" (ByVal pDestination As String, ByVal pSource As String, ByVal iMaxLength As Integer) As Long
Private Declare Function apiStringLen Lib "Kernel32" Alias "lstrlenW" (ByVal lpString As Long) As Long
Private Declare Function apiStrTrimW Lib "ShlwApi" Alias "StrTrimW" () As Boolean
Private Declare Function apiTerminateProcess Lib "Kernel32" Alias "TerminateProcess" (ByVal hWnd As Long, ByVal uExitCode As Long) As Long
Private Declare Function apiTimeGetTime Lib "Winmm" Alias "timeGetTime" () As Long
Private Declare Function apiVarPtrArray Lib "MsVbVm50" Alias "VarPtr" (Var() As Any) As Long
Private Declare Function apiWaitForSingleObject Lib "Kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Type browseInfo 'used by apiBrowseForFolder
hOwner As Long
pidlRoot As Long
pszDisplayName As String
lpszTitle As String
ulFlags As Long
lpfn As Long
lParam As Long
iImage As Long
End Type
Private Declare Function apiBrowseForFolder Lib "Shell32" Alias "SHBrowseForFolderA" (lpBrowseInfo As browseInfo) As Long
Private Type CHOOSECOLOR 'used by apiChooseColor; http://support.microsoft.com/kb/153929 and http://www.cpearson.com/Excel/Colors.aspx
lStructSize As Long
hWndOwner As Long
hInstance As Long
rgbResult As Long
lpCustColors As String
flags As Long
lCustData As Long
lpfnHook As Long
lpTemplateName As String
End Type
Private Declare Function apiChooseColor Lib "ComDlg32" Alias "ChooseColorA" (pChoosecolor As CHOOSECOLOR) As Long
Private Type FindWindowParameters 'Custom structure for passing in the parameters in/out of the hook enumeration function; could use global variables instead, but this is nicer
strTitle As String 'INPUT
hWnd As Long 'OUTPUT
End Type 'Find a specific window with dynamic caption from a list of all open windows: http://www.everythingaccess.com/tutorials.asp?ID=Bring-an-external-application-window-to-the-foreground
Private Declare Function apiEnumWindows Lib "User32" Alias "EnumWindows" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Private Type lastInputInfo 'used by apiGetLastInputInfo, getLastInputTime
cbSize As Long
dwTime As Long
End Type
Private Declare Function apiGetLastInputInfo Lib "User32" Alias "GetLastInputInfo" (ByRef plii As lastInputInfo) As Long
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
Private Declare Sub apiGetLocalTime Lib "Kernel32" Alias "GetLocalTime" (lpSystem As SystemTime)
Private Type pointAPI
X As Long
Y As Long
End Type
Private Type rectAPI
Left_Renamed As Long
Top_Renamed As Long
Right_Renamed As Long
Bottom_Renamed As Long
End Type
Private Type winPlacement
length As Long
flags As Long
showCmd As Long
ptMinPosition As pointAPI
ptMaxPosition As pointAPI
rcNormalPosition As rectAPI
End Type
Private Declare Function apiGetWindowPlacement Lib "User32" Alias "GetWindowPlacement" (ByVal hWnd As Long, ByRef lpwndpl As winPlacement) As Long
Private Type winRect
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function apiMoveWindow Lib "User32" Alias "MoveWindow" (ByVal hWnd As Long, xLeft As Long, ByVal yTop As Long, wWidth As Long, ByVal hHeight As Long, ByVal repaint As Long) As Long
#Else ' Win16 = True
#End If
Mac API
Microsoft официально не поддерживает API, но с некоторыми исследованиями больше объявлений можно найти в Интернете
Office 2016 для Mac изолирован песочницей
В отличие от других версий приложений Office, поддерживающих VBA, приложения Office 2016 для Mac изолированы.
Песочница ограничивает доступ приложений к ресурсам за пределами контейнера приложения. Это влияет на любые надстройки или макросы, которые связаны с доступом к файлам или связью между процессами. Вы можете минимизировать эффекты песочницы, используя новые команды, описанные в следующем разделе. Новые команды VBA для Office 2016 для Mac
Следующие команды VBA являются новыми и уникальными для Office 2016 для Mac.
команда | Используйте для |
---|---|
GrantAccessToMultipleFiles | Запросить разрешение пользователя на доступ к нескольким файлам одновременно |
AppleScriptTask | Вызовите внешние скрипты AppleScript от VB |
MAC_OFFICE_VERSION | IFDEF между различными версиями Mac Office во время компиляции |
Office 2011 для Mac
Private Declare Function system Lib "libc.dylib" (ByVal command As String) As Long
Private Declare Function popen Lib "libc.dylib" (ByVal command As String, ByVal mode As String) As Long
Private Declare Function pclose Lib "libc.dylib" (ByVal file As Long) As Long
Private Declare Function fread Lib "libc.dylib" (ByVal outStr As String, ByVal size As Long, ByVal items As Long, ByVal stream As Long) As Long
Private Declare Function feof Lib "libc.dylib" (ByVal file As Long) As Long
Office 2016 для Mac
Private Declare PtrSafe Function popen Lib "libc.dylib" (ByVal command As String, ByVal mode As String) As LongPtr
Private Declare PtrSafe Function pclose Lib "libc.dylib" (ByVal file As LongPtr) As Long
Private Declare PtrSafe Function fread Lib "libc.dylib" (ByVal outStr As String, ByVal size As LongPtr, ByVal items As LongPtr, ByVal stream As LongPtr) As Long
Private Declare PtrSafe Function feof Lib "libc.dylib" (ByVal file As LongPtr) As LongPtr
Получить общие мониторы и разрешение экрана
Option Explicit
'GetSystemMetrics32 info: http://msdn.microsoft.com/en-us/library/ms724385(VS.85).aspx
#If Win64 Then
Private Declare PtrSafe Function GetSystemMetrics32 Lib "User32" Alias "GetSystemMetrics" (ByVal nIndex As Long) As Long
#ElseIf Win32 Then
Private Declare Function GetSystemMetrics32 Lib "User32" Alias "GetSystemMetrics" (ByVal nIndex As Long) As Long
#End If
'VBA Wrappers:
Public Function dllGetMonitors() As Long
Const SM_CMONITORS = 80
dllGetMonitors = GetSystemMetrics32(SM_CMONITORS)
End Function
Public Function dllGetHorizontalResolution() As Long
Const SM_CXVIRTUALSCREEN = 78
dllGetHorizontalResolution = GetSystemMetrics32(SM_CXVIRTUALSCREEN)
End Function
Public Function dllGetVerticalResolution() As Long
Const SM_CYVIRTUALSCREEN = 79
dllGetVerticalResolution = GetSystemMetrics32(SM_CYVIRTUALSCREEN)
End Function
Public Sub ShowDisplayInfo()
Debug.Print "Total monitors: " & vbTab & vbTab & dllGetMonitors
Debug.Print "Horizontal Resolution: " & vbTab & dllGetHorizontalResolution
Debug.Print "Vertical Resolution: " & vbTab & dllGetVerticalResolution
'Total monitors: 1
'Horizontal Resolution: 1920
'Vertical Resolution: 1080
End Sub
FTP и региональные API
modFTP
Option Explicit
Option Compare Text
Option Private Module
'http://msdn.microsoft.com/en-us/library/aa384180(v=VS.85).aspx
'http://www.dailydoseofexcel.com/archives/2006/01/29/ftp-via-vba/
'http://www.15seconds.com/issue/981203.htm
'Open the Internet object
Private Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" ( _
ByVal sAgent As String, _
ByVal lAccessType As Long, _
ByVal sProxyName As String, _
ByVal sProxyBypass As String, _
ByVal lFlags As Long _
) As Long
'ex: lngINet = InternetOpen(“MyFTP Control”, 1, vbNullString, vbNullString, 0)
'Connect to the network
Private Declare Function InternetConnect Lib "wininet.dll" Alias "InternetConnectA" ( _
ByVal hInternetSession As Long, _
ByVal sServerName As String, _
ByVal nServerPort As Integer, _
ByVal sUsername As String, _
ByVal sPassword As String, _
ByVal lService As Long, _
ByVal lFlags As Long, _
ByVal lContext As Long _
) As Long
'ex: lngINetConn = InternetConnect(lngINet, "ftp.microsoft.com", 0, "anonymous", "[email protected]", 1, 0, 0)
'Get a file
Private Declare Function FtpGetFile Lib "wininet.dll" Alias "FtpGetFileA" ( _
ByVal hFtpSession As Long, _
ByVal lpszRemoteFile As String, _
ByVal lpszNewFile As String, _
ByVal fFailIfExists As Boolean, _
ByVal dwFlagsAndAttributes As Long, _
ByVal dwFlags As Long, _
ByVal dwContext As Long _
) As Boolean
'ex: blnRC = FtpGetFile(lngINetConn, "dirmap.txt", "c:dirmap.txt", 0, 0, 1, 0)
'Send a file
Private Declare Function FtpPutFile Lib "wininet.dll" Alias "FtpPutFileA" _
( _
ByVal hFtpSession As Long, _
ByVal lpszLocalFile As String, _
ByVal lpszRemoteFile As String, _
ByVal dwFlags As Long, ByVal dwContext As Long _
) As Boolean
'ex: blnRC = FtpPutFile(lngINetConn, “c:dirmap.txt”, “dirmap.txt”, 1, 0)
'Delete a file
Private Declare Function FtpDeleteFile Lib "wininet.dll" Alias "FtpDeleteFileA" _
( _
ByVal hFtpSession As Long, _
ByVal lpszFileName As String _
) As Boolean
'ex: blnRC = FtpDeleteFile(lngINetConn, “test.txt”)
'Close the Internet object
Private Declare Function InternetCloseHandle Lib "wininet.dll" (ByVal hInet As Long) As Integer
'ex: InternetCloseHandle lngINetConn
'ex: InternetCloseHandle lngINet
Private Declare Function FtpFindFirstFile Lib "wininet.dll" Alias "FtpFindFirstFileA" _
( _
ByVal hFtpSession As Long, _
ByVal lpszSearchFile As String, _
lpFindFileData As WIN32_FIND_DATA, _
ByVal dwFlags As Long, _
ByVal dwContent As Long _
) As Long
Private Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Private Type WIN32_FIND_DATA
dwFileAttributes As Long
ftCreationTime As FILETIME
ftLastAccessTime As FILETIME
ftLastWriteTime As FILETIME
nFileSizeHigh As Long
nFileSizeLow As Long
dwReserved0 As Long
dwReserved1 As Long
cFileName As String * MAX_FTP_PATH
cAlternate As String * 14
End Type
'ex: lngHINet = FtpFindFirstFile(lngINetConn, "*.*", pData, 0, 0)
Private Declare Function InternetFindNextFile Lib "wininet.dll" Alias "InternetFindNextFileA" _
( _
ByVal hFind As Long, _
lpvFindData As WIN32_FIND_DATA _
) As Long
'ex: blnRC = InternetFindNextFile(lngHINet, pData)
Public Sub showLatestFTPVersion()
Dim ftpSuccess As Boolean, msg As String, lngFindFirst As Long
Dim lngINet As Long, lngINetConn As Long
Dim pData As WIN32_FIND_DATA
'init the filename buffer
pData.cFileName = String(260, 0)
msg = "FTP Error"
lngINet = InternetOpen("MyFTP Control", 1, vbNullString, vbNullString, 0)
If lngINet > 0 Then
lngINetConn = InternetConnect(lngINet, FTP_SERVER_NAME, FTP_SERVER_PORT, FTP_USER_NAME, FTP_PASSWORD, 1, 0, 0)
If lngINetConn > 0 Then
FtpPutFile lngINetConn, "C:Tmpftp.cls", "ftp.cls", FTP_TRANSFER_BINARY, 0
'lngFindFirst = FtpFindFirstFile(lngINetConn, "ExcelDiff.xlsm", pData, 0, 0)
If lngINet = 0 Then
msg = "DLL error: " & Err.LastDllError & ", Error Number: " & Err.Number & ", Error Desc: " & Err.Description
Else
msg = left(pData.cFileName, InStr(1, pData.cFileName, String(1, 0), vbBinaryCompare) - 1)
End If
InternetCloseHandle lngINetConn
End If
InternetCloseHandle lngINet
End If
MsgBox msg
End Sub
modRegional:
Option Explicit
Private Const LOCALE_SDECIMAL = &HE
Private Const LOCALE_SLIST = &HC
Private Declare Function GetLocaleInfo Lib "Kernel32" Alias "GetLocaleInfoA" (ByVal Locale As Long, ByVal LCType As Long, ByVal lpLCData As String, ByVal cchData As Long) As Long
Private Declare Function SetLocaleInfo Lib "Kernel32" Alias "SetLocaleInfoA" (ByVal Locale As Long, ByVal LCType As Long, ByVal lpLCData As String) As Boolean
Private Declare Function GetUserDefaultLCID% Lib "Kernel32" ()
Public Function getTimeSeparator() As String
getTimeSeparator = Application.International(xlTimeSeparator)
End Function
Public Function getDateSeparator() As String
getDateSeparator = Application.International(xlDateSeparator)
End Function
Public Function getListSeparator() As String
Dim ListSeparator As String, iRetVal1 As Long, iRetVal2 As Long, lpLCDataVar As String, Position As Integer, Locale As Long
Locale = GetUserDefaultLCID()
iRetVal1 = GetLocaleInfo(Locale, LOCALE_SLIST, lpLCDataVar, 0)
ListSeparator = String$(iRetVal1, 0)
iRetVal2 = GetLocaleInfo(Locale, LOCALE_SLIST, ListSeparator, iRetVal1)
Position = InStr(ListSeparator, Chr$(0))
If Position > 0 Then ListSeparator = Left$(ListSeparator, Position - 1) Else ListSeparator = vbNullString
getListSeparator = ListSeparator
End Function
Private Sub ChangeSettingExample() 'change the setting of the character displayed as the decimal separator.
Call SetLocalSetting(LOCALE_SDECIMAL, ",") 'to change to ","
Stop 'check your control panel to verify or use the GetLocaleInfo API function
Call SetLocalSetting(LOCALE_SDECIMAL, ".") 'to back change to "."
End Sub
Private Function SetLocalSetting(LC_CONST As Long, Setting As String) As Boolean
Call SetLocaleInfo(GetUserDefaultLCID(), LC_CONST, Setting)
End Function
Estimated reading time: 7 minutes
Table of contents
- Windows API embraces VBA
- Why VBA programmers need this?
- How to declare a Windows API function
- Practical Windows API Tips for the VBA Programmer
- How to Deal with 32-bit Excel and 64-bit Excel (2021 edit)
- Now try an easy exercise (2021 edit)
- Will Windows API in VBA work on MacOS? (2021 edit)
- Most useful Windows APIs for VBA programming (2021 edit)
Windows API embraces VBA
The Windows Application Programming Interface (Windows API) is a set of functions within the Microsoft Windows operating system that are available to Windows programmers. Fortunately, the Windows API has always exposed a large part of the underlying structure of the Windows operating systems to VBA programmers too!
Why VBA programmers need this?
Often times a VBA programmer may not be able to find a suitable Excel or a native VBA language function to perform a task but is available within the Windows APIs. For example, VBA does not have a function to measure time in milliseconds but it is available in Windows API (I will write a tutorial on microsecond timer in VBA). In such cases, a VBA program is able to invoke an Windows API by declaring the Windows API function at the top of a VBA code module.
How to declare a Windows API function
A Windows API function must be declared correctly at the top of a VBA code module. The declaration statement will tell the VBA virtual machine 3 important pieces of information.
1. The Window API function name you would like to use.
2. The Library location of the the Windows API function.
3. The Window API function’s arguments.
Here is an example:
Declare PtrSafe Function GetSystemDirectory Lib "kernel32" _
Alias "GetSystemDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) As Long
Let’s try to break this down into plain English.
Declare: A Windows API declaration always start with the “Declare” keyword.
PtrSafe: By adding PtrSafe after the Declare keyword, it makes this API work with 64-bit Excel. If you are writing VBA code in 32-bit Excel, this keyword should not be used.
Function GetSystemDirectory Lib “kernel32”: This tells VBA that GetSystemDirectory is the name of Windows API that will be used in your VBA code and this Windows API is located in a library called “kernel32”.
Alias “GetSystemDirectoryA”: This means within the “kernel32” library, the Windows API’s actual name is GetSystemDirectoryA.
(ByVal lpBuffer As String, ByVal nSize As Long) As Long: After calling the Windows API, the systems directory path is contained in lpBuffer, and the length of the directory string is contained in nSize. The Windows API only returns a number indicating the length of the directory string.
Below is a VBA subroutine to use this Windows API:
'***************************************************
'Purpose: invoke the Windows API GetSystemDirectoryA
'***************************************************
Sub SystemsDir()
Dim strSysPath As String * 255 'declare a fixed length string of 255 characters
MsgBox Left(strSysPath, GetSystemDirectory(strSysPath, 255))
End Sub
Practical Windows API Tips for the VBA Programmer
It is essential to get the Windows API declaration exactly correct. There are many resource in the web that lists down the declarations for Windows API calls. The VBA programmer only needs to copy the declarations and use the functions without understanding the details. As a useful resource you can obtain a list of Windows API declarations for VBA by searching for this file “Win32API_PtrSafe.TXT”. Or you can download it here (don’t worry it’s a plain text file).
How to Deal with 32-bit Excel and 64-bit Excel (2021 edit)
Tip 1: Use PtrSafe for 64-bit Excel
To ensure a Windows API function call will work in 64-bit Excel, we need the keyword “PtrSafe” somewhere in the Windows API declaration. Without this, you will get an error.
Tip 2: Use LongPtr for 64-bit Excel
VBA now includes the variable type alias LongPtr. The actual data type that LongPtr resolves to depends on the version of Office that it is running in; LongPtr resolves to Long in 32-bit versions of Office. For Windows API code to work in 64-bit versions of Office, replace all Long and LongLong datatypes to LongPtr.
Tip 3: Use conditional compiler directives
If you need your WIndows API function call to be compatible in both 32-bit and 64-bit Excel, we need to declare two versions of the function declaration by using conditional compiler directives.
The following is an example to show all 3 tips in action. The codes in bold are the recommended changes for 64-bit Excel. You can read the long and complicated reference from Microsoft: 64-bit Visual Basic for Applications overview (if you want).
#If VBA7 Then
'for 64-bit Excel
Declare PtrSafe Function GetSystemDirectory Lib "kernel32" _
Alias "GetSystemDirectoryA" (ByVal lpBuffer As String, ByVal nSize As LongPtr) As LongPtr
#Else
'for 32-bit Excel
Declare Function GetSystemDirectory Lib "kernel32" _
Alias "GetSystemDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) As Long
#End If
Now try an easy exercise (2021 edit)
A very useful Windows API to use in your VBA project is the Sleep Windows API. It suspends the execution of the your VBA subroutine until the time-out interval elapses. A quick search within the file “Win32API_PtrSafe.TXT” shows this Windows API:
Declare PtrSafe Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
So how would this Sleep Windows API be used within a VBA code module? It’s a simple 2 step process.
Step 1: Write the Windows API declaration statement at the top of a code module. You need to wrap the declaration in some preprocessor directives to handle both 32 bit Excel and 64 bit Excel environments. Don’t forget to add the extra keyword PtrSafe and replace all Long data type with LongPtr for 64-bit Excel.
#If VBA7 Then
'for 64-bit Excel
Declare PtrSafe Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As LongPtr)
#Else
'for 32-bit Excel
Declare Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
#End If
Step 2: Now you can use “Sleep” just like a typical VBA function.
Sub Hello()
Sleep (1000) 'this is a Windows API not a VBA function!
MsgBox ("Oh, I slept for 1 sec.")
End Sub
Will Windows API in VBA work on MacOS? (2021 edit)
Window APIs in an Excel VBA application will not work on the MacOS. So to make your VBA code run safely on Windows OS and MacOS, it’s a good idea to check the operating system before executing any Windows API calls. You will need to limit yourself to pure VBA language functions or find an equivalent with AppleScript if you are working with MacOS.
#If VBA7 Then
'for 64-bit Excel
Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#Else
'for 32-bit Excel
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If
Sub Hello()
If DetectVBAEnvironment = "win" Then 'check for Win OS
Sleep (1000) 'this is a Windows API for WinOS
MsgBox ("Slept for 1000 ms")
else
Application.Wait Now + TimeValue("0:00:01") 'this is a VBA call for MacOS
End If
End Sub
'Purpose: detects the OS
Function DetectVBAEnvironment()
Dim s As String
s = LCase(Application.OperatingSystem)
If InStr(s, "mac") Then
DetectVBAEnvironment = "mac"
Else
DetectVBAEnvironment = "win"
End If
End Function
Most useful Windows APIs for VBA programming (2021 edit)
There is a very long list of WIndows APIs available. This is my short & useful list of of useful Windows APIs for the typical VBA programmer:
Sleep. Suspends execution for a specified period. It places the running code into an inactive state for the number of milliseconds passed to the function.
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Sleep milliseconds
GetUserName. If you need to know who’s logged into an Access database or Exel workbook, use GetUserName.
Private Declare Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long
GetComputerName. This is similar to GetUserName except it retrieves the system’s name.
Private Declare Function GetComputerName Lib "kernel32" Alias "GetComputerNameA" (ByVal lpBuffer As String, nSize As Long) As Long
GetFileSize. Retrieves the size of the specified file, in bytes.
Declare PtrSafe Function GetFileSize Lib "kernel32" Alias "GetFileSize" (ByVal hFile As LongPtr, lpFileSizeHigh As Long) As Long
GetDiskFreeSpace. Retrieves information about the specified disk, including the amount of free space on the disk.
Declare PtrSafe Function GetDiskFreeSpace Lib "kernel32" Alias "GetDiskFreeSpaceA" (ByVal lpRootPathName As String, lpSectorsPerCluster As Long, lpBytesPerSector As Long, lpNumberOfFreeClusters As Long, lpTotalNumberOfClusters As Long) As Long
Aeternus Consulting is the premier training centre in Singapore for Excel Courses – Basic Excel, Advanced Excel and Excel VBA Macro courses. For more Microsoft Excel training courses, please visit our Excel training page.
Aeternus Consulting is now on Instagram! So take a look and follow us on Instagram.